[WEB-5230 | WEB-5231] chore: new empty state implementation (#7972)
This commit is contained in:
parent
a60d74a3c0
commit
68fd2463f4
72 changed files with 5260 additions and 746 deletions
204
packages/propel/src/empty-state/assets-showcase.stories.tsx
Normal file
204
packages/propel/src/empty-state/assets-showcase.stories.tsx
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import { HorizontalStackAssetsMap } from "./assets/horizontal-stack/constant";
|
||||
import { IllustrationMap } from "./assets/illustration/constant";
|
||||
import { VerticalStackAssetsMap } from "./assets/vertical-stack/constant";
|
||||
|
||||
// Meta for asset showcase
|
||||
const meta: Meta = {
|
||||
title: "Components/EmptyState/Assets Showcase",
|
||||
parameters: {
|
||||
layout: "fullscreen",
|
||||
docs: {
|
||||
description: {
|
||||
component: "Visual catalog of all available empty state assets organized by type.",
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj;
|
||||
|
||||
export const HorizontalStackAssets: Story = {
|
||||
parameters: {
|
||||
docs: {
|
||||
description: {
|
||||
story:
|
||||
"Horizontal stack assets designed for compact empty states. These are optimized for smaller, inline empty state scenarios.",
|
||||
},
|
||||
},
|
||||
},
|
||||
render: () => (
|
||||
<div className="p-8">
|
||||
<div className="mb-6">
|
||||
<h2 className="text-xl font-semibold text-custom-text-100">Horizontal Stack Assets</h2>
|
||||
<p className="text-sm text-custom-text-300">Used primarily in EmptyStateCompact component</p>
|
||||
</div>
|
||||
<div className="grid w-full grid-cols-12 gap-6">
|
||||
{HorizontalStackAssetsMap.map((item) => (
|
||||
<div
|
||||
key={item.title}
|
||||
className="col-span-6 flex flex-col items-center justify-center gap-3 rounded-lg border border-custom-border-200 bg-custom-background-100 p-6 sm:col-span-4 lg:col-span-3"
|
||||
>
|
||||
<div className="flex h-24 w-24 items-center justify-center">{item.asset}</div>
|
||||
<p className="text-center text-xs font-medium text-custom-text-200">{item.title}</p>
|
||||
<code className="rounded bg-custom-background-80 px-2 py-1 text-xs text-custom-text-300">
|
||||
{item.title.toLowerCase().replace(/\s+/g, "-")}
|
||||
</code>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
};
|
||||
|
||||
export const VerticalStackAssets: Story = {
|
||||
parameters: {
|
||||
docs: {
|
||||
description: {
|
||||
story:
|
||||
"Vertical stack assets designed for detailed empty states. These are larger and more prominent, suitable for feature-specific empty states.",
|
||||
},
|
||||
},
|
||||
},
|
||||
render: () => (
|
||||
<div className="p-8">
|
||||
<div className="mb-6">
|
||||
<h2 className="text-xl font-semibold text-custom-text-100">Vertical Stack Assets</h2>
|
||||
<p className="text-sm text-custom-text-300">Used primarily in EmptyStateDetailed component</p>
|
||||
</div>
|
||||
<div className="grid w-full grid-cols-12 gap-6">
|
||||
{VerticalStackAssetsMap.map((item) => (
|
||||
<div
|
||||
key={item.title}
|
||||
className="col-span-6 flex flex-col items-center justify-center gap-3 rounded-lg border border-custom-border-200 bg-custom-background-100 p-6 sm:col-span-4 lg:col-span-3"
|
||||
>
|
||||
<div className="flex h-32 w-32 items-center justify-center">{item.asset}</div>
|
||||
<p className="text-center text-xs font-medium text-custom-text-200">
|
||||
{item.title.replace(/VerticalStackIllustration$/, "")}
|
||||
</p>
|
||||
<code className="rounded bg-custom-background-80 px-2 py-1 text-xs text-custom-text-300">
|
||||
{item.title
|
||||
.replace(/VerticalStackIllustration$/, "")
|
||||
.replace(/([A-Z])/g, "-$1")
|
||||
.toLowerCase()
|
||||
.slice(1)}
|
||||
</code>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
};
|
||||
|
||||
export const IllustrationAssets: Story = {
|
||||
parameters: {
|
||||
docs: {
|
||||
description: {
|
||||
story: "Illustration assets available for both compact and detailed empty states.",
|
||||
},
|
||||
},
|
||||
},
|
||||
render: () => (
|
||||
<div className="p-8">
|
||||
<div className="mb-6">
|
||||
<h2 className="text-xl font-semibold text-custom-text-100">Illustration Assets</h2>
|
||||
<p className="text-sm text-custom-text-300">Available in both EmptyStateCompact and EmptyStateDetailed</p>
|
||||
</div>
|
||||
<div className="grid w-full grid-cols-12 gap-6">
|
||||
{IllustrationMap.map((item) => (
|
||||
<div
|
||||
key={item.title}
|
||||
className="col-span-6 flex flex-col items-center justify-center gap-3 rounded-lg border border-custom-border-200 bg-custom-background-100 p-6 sm:col-span-4 lg:col-span-3"
|
||||
>
|
||||
<div className="flex h-24 w-24 items-center justify-center">{item.asset}</div>
|
||||
<p className="text-center text-xs font-medium text-custom-text-200">{item.title}</p>
|
||||
<code className="rounded bg-custom-background-80 px-2 py-1 text-xs text-custom-text-300">
|
||||
{item.title.toLowerCase()}
|
||||
</code>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
};
|
||||
|
||||
export const AllAssets: Story = {
|
||||
parameters: {
|
||||
docs: {
|
||||
description: {
|
||||
story: "Complete catalog of all available empty state assets.",
|
||||
},
|
||||
},
|
||||
},
|
||||
render: () => (
|
||||
<div className="space-y-12 p-8">
|
||||
{/* Horizontal Stack */}
|
||||
<div>
|
||||
<div className="mb-6">
|
||||
<h2 className="text-xl font-semibold text-custom-text-100">Horizontal Stack Assets</h2>
|
||||
<p className="text-sm text-custom-text-300">
|
||||
For EmptyStateCompact - {HorizontalStackAssetsMap.length} assets
|
||||
</p>
|
||||
</div>
|
||||
<div className="grid w-full grid-cols-12 gap-4">
|
||||
{HorizontalStackAssetsMap.map((item) => (
|
||||
<div
|
||||
key={item.title}
|
||||
className="col-span-6 flex flex-col items-center justify-center gap-2 rounded border border-custom-border-200 bg-custom-background-100 p-4 sm:col-span-3 lg:col-span-2"
|
||||
>
|
||||
<div className="flex h-16 w-16 items-center justify-center">{item.asset}</div>
|
||||
<code className="text-[10px] text-custom-text-400">{item.title.toLowerCase().replace(/\s+/g, "-")}</code>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Vertical Stack */}
|
||||
<div>
|
||||
<div className="mb-6">
|
||||
<h2 className="text-xl font-semibold text-custom-text-100">Vertical Stack Assets</h2>
|
||||
<p className="text-sm text-custom-text-300">
|
||||
For EmptyStateDetailed - {VerticalStackAssetsMap.length} assets
|
||||
</p>
|
||||
</div>
|
||||
<div className="grid w-full grid-cols-12 gap-4">
|
||||
{VerticalStackAssetsMap.map((item) => (
|
||||
<div
|
||||
key={item.title}
|
||||
className="col-span-6 flex flex-col items-center justify-center gap-2 rounded border border-custom-border-200 bg-custom-background-100 p-4 sm:col-span-3 lg:col-span-2"
|
||||
>
|
||||
<div className="flex h-20 w-20 items-center justify-center">{item.asset}</div>
|
||||
<code className="text-center text-[10px] text-custom-text-400">
|
||||
{item.title
|
||||
.replace(/VerticalStackIllustration$/, "")
|
||||
.replace(/([A-Z])/g, "-$1")
|
||||
.toLowerCase()
|
||||
.slice(1)}
|
||||
</code>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Illustrations */}
|
||||
<div>
|
||||
<div className="mb-6">
|
||||
<h2 className="text-xl font-semibold text-custom-text-100">Illustration Assets</h2>
|
||||
<p className="text-sm text-custom-text-300">For both components - {IllustrationMap.length} assets</p>
|
||||
</div>
|
||||
<div className="grid w-full grid-cols-12 gap-4">
|
||||
{IllustrationMap.map((item) => (
|
||||
<div
|
||||
key={item.title}
|
||||
className="col-span-6 flex flex-col items-center justify-center gap-2 rounded border border-custom-border-200 bg-custom-background-100 p-4 sm:col-span-3 lg:col-span-2"
|
||||
>
|
||||
<div className="flex h-16 w-16 items-center justify-center">{item.asset}</div>
|
||||
<code className="text-[10px] text-custom-text-400">{item.title.toLowerCase()}</code>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
};
|
||||
130
packages/propel/src/empty-state/assets/asset-registry.tsx
Normal file
130
packages/propel/src/empty-state/assets/asset-registry.tsx
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
import React from "react";
|
||||
import type {
|
||||
CompactAssetType,
|
||||
DetailedAssetType,
|
||||
HorizontalStackAssetType,
|
||||
IllustrationAssetType,
|
||||
VerticalStackAssetType,
|
||||
} from "./asset-types";
|
||||
import {
|
||||
CustomerHorizontalStackIllustration,
|
||||
EpicHorizontalStackIllustration,
|
||||
EstimateHorizontalStackIllustration,
|
||||
ExportHorizontalStackIllustration,
|
||||
IntakeHorizontalStackIllustration,
|
||||
LabelHorizontalStackIllustration,
|
||||
LinkHorizontalStackIllustration,
|
||||
MembersHorizontalStackIllustration,
|
||||
NoteHorizontalStackIllustration,
|
||||
PriorityHorizontalStackIllustration,
|
||||
ProjectHorizontalStackIllustration,
|
||||
SettingsHorizontalStackIllustration,
|
||||
StateHorizontalStackIllustration,
|
||||
TemplateHorizontalStackIllustration,
|
||||
TokenHorizontalStackIllustration,
|
||||
UnknownHorizontalStackIllustration,
|
||||
UpdateHorizontalStackIllustration,
|
||||
WebhookHorizontalStackIllustration,
|
||||
WorkItemHorizontalStackIllustration,
|
||||
WorklogHorizontalStackIllustration,
|
||||
} from "./horizontal-stack";
|
||||
import { InboxIllustration, SearchIllustration } from "./illustration";
|
||||
import {
|
||||
ArchivedCycleVerticalStackIllustration,
|
||||
ArchivedModuleVerticalStackIllustration,
|
||||
ArchivedWorkItemVerticalStackIllustration,
|
||||
CustomerVerticalStackIllustration,
|
||||
CycleVerticalStackIllustration,
|
||||
DashboardVerticalStackIllustration,
|
||||
DraftVerticalStackIllustration,
|
||||
EpicVerticalStackIllustration,
|
||||
Error404VerticalStackIllustration,
|
||||
InvalidLinkVerticalStackIllustration,
|
||||
ModuleVerticalStackIllustration,
|
||||
NoAccessVerticalStackIllustration,
|
||||
PageVerticalStackIllustration,
|
||||
ProjectVerticalStackIllustration,
|
||||
ServerErrorVerticalStackIllustration,
|
||||
TeamspaceVerticalStackIllustration,
|
||||
ViewVerticalStackIllustration,
|
||||
WorkItemVerticalStackIllustration,
|
||||
} from "./vertical-stack";
|
||||
|
||||
// Horizontal Stack Asset Registry
|
||||
export const HORIZONTAL_STACK_ASSETS: Record<HorizontalStackAssetType, React.ComponentType<{ className?: string }>> = {
|
||||
customer: CustomerHorizontalStackIllustration,
|
||||
epic: EpicHorizontalStackIllustration,
|
||||
estimate: EstimateHorizontalStackIllustration,
|
||||
export: ExportHorizontalStackIllustration,
|
||||
intake: IntakeHorizontalStackIllustration,
|
||||
label: LabelHorizontalStackIllustration,
|
||||
link: LinkHorizontalStackIllustration,
|
||||
members: MembersHorizontalStackIllustration,
|
||||
note: NoteHorizontalStackIllustration,
|
||||
priority: PriorityHorizontalStackIllustration,
|
||||
project: ProjectHorizontalStackIllustration,
|
||||
settings: SettingsHorizontalStackIllustration,
|
||||
state: StateHorizontalStackIllustration,
|
||||
template: TemplateHorizontalStackIllustration,
|
||||
token: TokenHorizontalStackIllustration,
|
||||
unknown: UnknownHorizontalStackIllustration,
|
||||
update: UpdateHorizontalStackIllustration,
|
||||
webhook: WebhookHorizontalStackIllustration,
|
||||
"work-item": WorkItemHorizontalStackIllustration,
|
||||
worklog: WorklogHorizontalStackIllustration,
|
||||
};
|
||||
|
||||
// Vertical Stack Asset Registry
|
||||
export const VERTICAL_STACK_ASSETS: Record<VerticalStackAssetType, React.ComponentType<{ className?: string }>> = {
|
||||
"archived-cycle": ArchivedCycleVerticalStackIllustration,
|
||||
"archived-module": ArchivedModuleVerticalStackIllustration,
|
||||
"archived-work-item": ArchivedWorkItemVerticalStackIllustration,
|
||||
customer: CustomerVerticalStackIllustration,
|
||||
cycle: CycleVerticalStackIllustration,
|
||||
dashboard: DashboardVerticalStackIllustration,
|
||||
draft: DraftVerticalStackIllustration,
|
||||
epic: EpicVerticalStackIllustration,
|
||||
"error-404": Error404VerticalStackIllustration,
|
||||
"invalid-link": InvalidLinkVerticalStackIllustration,
|
||||
module: ModuleVerticalStackIllustration,
|
||||
"no-access": NoAccessVerticalStackIllustration,
|
||||
page: PageVerticalStackIllustration,
|
||||
project: ProjectVerticalStackIllustration,
|
||||
"server-error": ServerErrorVerticalStackIllustration,
|
||||
teamspace: TeamspaceVerticalStackIllustration,
|
||||
view: ViewVerticalStackIllustration,
|
||||
"work-item": WorkItemVerticalStackIllustration,
|
||||
};
|
||||
|
||||
// Illustration Asset Registry
|
||||
export const ILLUSTRATION_ASSETS: Record<IllustrationAssetType, React.ComponentType<{ className?: string }>> = {
|
||||
inbox: InboxIllustration,
|
||||
search: SearchIllustration,
|
||||
};
|
||||
|
||||
// Helper functions to get assets
|
||||
export const getCompactAsset = (assetKey: CompactAssetType, className?: string): React.ReactNode => {
|
||||
const AssetComponent =
|
||||
(HORIZONTAL_STACK_ASSETS[assetKey as HorizontalStackAssetType] as React.ComponentType<{ className?: string }>) ||
|
||||
ILLUSTRATION_ASSETS[assetKey as IllustrationAssetType];
|
||||
|
||||
if (!AssetComponent) {
|
||||
console.warn(`Asset "${assetKey}" not found in compact asset registry`);
|
||||
return null;
|
||||
}
|
||||
|
||||
return <AssetComponent className={className} />;
|
||||
};
|
||||
|
||||
export const getDetailedAsset = (assetKey: DetailedAssetType, className?: string): React.ReactNode => {
|
||||
const AssetComponent =
|
||||
(VERTICAL_STACK_ASSETS[assetKey as VerticalStackAssetType] as React.ComponentType<{ className?: string }>) ||
|
||||
ILLUSTRATION_ASSETS[assetKey as IllustrationAssetType];
|
||||
|
||||
if (!AssetComponent) {
|
||||
console.warn(`Asset "${assetKey}" not found in detailed asset registry`);
|
||||
return null;
|
||||
}
|
||||
|
||||
return <AssetComponent className={className} />;
|
||||
};
|
||||
52
packages/propel/src/empty-state/assets/asset-types.ts
Normal file
52
packages/propel/src/empty-state/assets/asset-types.ts
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
// Horizontal Stack Asset Types
|
||||
export type HorizontalStackAssetType =
|
||||
| "customer"
|
||||
| "epic"
|
||||
| "estimate"
|
||||
| "export"
|
||||
| "intake"
|
||||
| "label"
|
||||
| "link"
|
||||
| "members"
|
||||
| "note"
|
||||
| "priority"
|
||||
| "project"
|
||||
| "settings"
|
||||
| "state"
|
||||
| "template"
|
||||
| "token"
|
||||
| "unknown"
|
||||
| "update"
|
||||
| "webhook"
|
||||
| "work-item"
|
||||
| "worklog";
|
||||
|
||||
// Vertical Stack Asset Types
|
||||
export type VerticalStackAssetType =
|
||||
| "archived-cycle"
|
||||
| "archived-module"
|
||||
| "archived-work-item"
|
||||
| "customer"
|
||||
| "cycle"
|
||||
| "dashboard"
|
||||
| "draft"
|
||||
| "epic"
|
||||
| "error-404"
|
||||
| "invalid-link"
|
||||
| "module"
|
||||
| "no-access"
|
||||
| "page"
|
||||
| "project"
|
||||
| "server-error"
|
||||
| "teamspace"
|
||||
| "view"
|
||||
| "work-item";
|
||||
|
||||
// Illustration Asset Types
|
||||
export type IllustrationAssetType = "inbox" | "search";
|
||||
|
||||
// Combined Asset Types for Compact (uses horizontal + illustration)
|
||||
export type CompactAssetType = HorizontalStackAssetType | IllustrationAssetType;
|
||||
|
||||
// Combined Asset Types for Detailed (uses vertical + illustration)
|
||||
export type DetailedAssetType = VerticalStackAssetType | IllustrationAssetType;
|
||||
|
|
@ -9,6 +9,7 @@ import {
|
|||
MembersHorizontalStackIllustration,
|
||||
NoteHorizontalStackIllustration,
|
||||
PriorityHorizontalStackIllustration,
|
||||
ProjectHorizontalStackIllustration,
|
||||
SettingsHorizontalStackIllustration,
|
||||
StateHorizontalStackIllustration,
|
||||
TemplateHorizontalStackIllustration,
|
||||
|
|
@ -61,6 +62,10 @@ export const HorizontalStackAssetsMap = [
|
|||
asset: <PriorityHorizontalStackIllustration className="w-20 h-20" />,
|
||||
title: "Priority",
|
||||
},
|
||||
{
|
||||
asset: <ProjectHorizontalStackIllustration className="w-20 h-20" />,
|
||||
title: "Project",
|
||||
},
|
||||
{
|
||||
asset: <SettingsHorizontalStackIllustration className="w-20 h-20" />,
|
||||
title: "Settings",
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ export * from "./link";
|
|||
export * from "./members";
|
||||
export * from "./note";
|
||||
export * from "./priority";
|
||||
export * from "./project";
|
||||
export * from "./settings";
|
||||
export * from "./state";
|
||||
export * from "./template";
|
||||
|
|
|
|||
|
|
@ -0,0 +1,69 @@
|
|||
import { type TIllustrationAssetProps, ILLUSTRATION_COLOR_TOKEN_MAP } from "../helper";
|
||||
|
||||
export const ProjectHorizontalStackIllustration = ({ className }: TIllustrationAssetProps) => (
|
||||
<svg
|
||||
width="102"
|
||||
height="115"
|
||||
viewBox="0 0 102 115"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={className}
|
||||
>
|
||||
<g opacity="0.2">
|
||||
<path
|
||||
d="M60.2802 2.91929C59.1202 2.32929 57.6302 2.37929 56.0102 3.19929L11.3502 25.9593C7.68023 27.8293 4.70023 32.9693 4.70023 37.4293V91.7793C4.70023 94.2693 5.62023 96.0193 7.07023 96.7693L2.61023 94.4993C1.16023 93.7593 0.240234 91.9993 0.240234 89.5093V35.1593C0.240234 30.6893 3.22023 25.5593 6.89023 23.6893L51.5602 0.929289C53.1902 0.0992895 54.6802 0.0592895 55.8302 0.649289L60.2902 2.91929H60.2802Z"
|
||||
fill={ILLUSTRATION_COLOR_TOKEN_MAP.fill.secondary}
|
||||
stroke={ILLUSTRATION_COLOR_TOKEN_MAP.stroke.secondary}
|
||||
strokeWidth="0.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M60.2802 2.91882C61.7302 3.65882 62.6502 5.41882 62.6502 7.90882V62.2588C62.6502 66.7288 59.6702 71.8588 56.0002 73.7288L11.3402 96.4888C9.7102 97.3188 8.2202 97.3588 7.0702 96.7688C5.6202 96.0288 4.7002 94.2688 4.7002 91.7788V37.4288C4.7002 32.9588 7.6802 27.8288 11.3502 25.9588L56.0102 3.19882C57.6402 2.36882 59.1302 2.32882 60.2802 2.91882Z"
|
||||
fill={ILLUSTRATION_COLOR_TOKEN_MAP.fill.primary}
|
||||
stroke={ILLUSTRATION_COLOR_TOKEN_MAP.stroke.secondary}
|
||||
strokeWidth="0.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</g>
|
||||
<g opacity="0.6">
|
||||
<path
|
||||
d="M79.7705 11.5892C78.6105 10.9992 77.1205 11.0492 75.5005 11.8692L30.8405 34.6292C27.1705 36.4992 24.1905 41.6392 24.1905 46.0992V100.449C24.1905 102.939 25.1105 104.689 26.5605 105.439L22.1005 103.169C20.6505 102.429 19.7305 100.669 19.7305 98.1792V43.8292C19.7305 39.3592 22.7105 34.2292 26.3805 32.3592L71.0405 9.59921C72.6705 8.76921 74.1605 8.72921 75.3105 9.31921L79.7705 11.5892Z"
|
||||
fill={ILLUSTRATION_COLOR_TOKEN_MAP.fill.secondary}
|
||||
stroke={ILLUSTRATION_COLOR_TOKEN_MAP.stroke.secondary}
|
||||
strokeWidth="0.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M79.7704 11.5887C81.2204 12.3287 82.1404 14.0887 82.1404 16.5787V70.9287C82.1404 75.3987 79.1604 80.5287 75.4904 82.3987L30.8304 105.159C29.2004 105.989 27.7104 106.029 26.5604 105.439C25.1104 104.699 24.1904 102.939 24.1904 100.449V46.0987C24.1904 41.6287 27.1704 36.4987 30.8404 34.6287L75.5004 11.8687C77.1304 11.0387 78.6204 10.9987 79.7704 11.5887Z"
|
||||
fill={ILLUSTRATION_COLOR_TOKEN_MAP.fill.primary}
|
||||
stroke={ILLUSTRATION_COLOR_TOKEN_MAP.stroke.secondary}
|
||||
strokeWidth="0.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</g>
|
||||
<path
|
||||
d="M99.2602 20.1986C98.1002 19.6086 96.6102 19.6586 94.9902 20.4786L50.3302 43.2386C46.6602 45.1086 43.6802 50.2486 43.6802 54.7086V109.059C43.6802 111.549 44.6002 113.299 46.0502 114.049L41.5902 111.779C40.1402 111.039 39.2202 109.279 39.2202 106.789V52.4386C39.2202 47.9686 42.2002 42.8386 45.8702 40.9686L90.5302 18.2086C92.1602 17.3786 93.6502 17.3386 94.8002 17.9286L99.2602 20.1986Z"
|
||||
fill={ILLUSTRATION_COLOR_TOKEN_MAP.fill.secondary}
|
||||
stroke={ILLUSTRATION_COLOR_TOKEN_MAP.stroke.secondary}
|
||||
strokeWidth="0.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M99.2602 20.1981C100.71 20.9381 101.63 22.6981 101.63 25.1881V79.5381C101.63 84.0081 98.6502 89.1381 94.9802 91.0081L50.3202 113.768C48.6902 114.598 47.2002 114.638 46.0502 114.048C44.6002 113.308 43.6802 111.548 43.6802 109.058V54.7081C43.6802 50.2381 46.6602 45.1081 50.3302 43.2381L94.9902 20.4781C96.6202 19.6481 98.1102 19.6081 99.2602 20.1981Z"
|
||||
fill={ILLUSTRATION_COLOR_TOKEN_MAP.fill.primary}
|
||||
stroke={ILLUSTRATION_COLOR_TOKEN_MAP.stroke.secondary}
|
||||
strokeWidth="0.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M85.8902 51.3399L81.9602 53.3399V51.7399C81.9602 49.0999 80.2002 47.8499 78.0302 48.9599L72.7802 51.6299C70.6102 52.7399 68.8502 55.7799 68.8502 58.4199V60.0199L64.9202 62.0199C62.7502 63.1299 60.9902 66.1699 60.9902 68.8099V84.7699C60.9902 87.4099 62.7502 88.6599 64.9202 87.5499L85.9002 76.8599C88.0702 75.7499 89.8302 72.7099 89.8302 70.0699V54.1099C89.8302 51.4699 88.0702 50.2199 85.9002 51.3299L85.8902 51.3399ZM71.4602 57.0899C71.4602 56.2099 72.0502 55.1899 72.7702 54.8299L78.0202 52.1599C78.7402 51.7899 79.3302 52.2099 79.3302 53.0899V54.6899L71.4602 58.6999V57.0999V57.0899ZM79.3302 57.8699V77.0199L71.4602 81.0299V61.8799L79.3302 57.8699ZM63.5902 83.4399V67.4799C63.5902 66.5999 64.1802 65.5799 64.9002 65.2199L68.8302 63.2199V82.3699L64.9002 84.3699C64.1802 84.7399 63.5902 84.3199 63.5902 83.4399ZM87.2002 71.4099C87.2002 72.2899 86.6102 73.3099 85.8902 73.6699L81.9602 75.6699V56.5199L85.8902 54.5199C86.6102 54.1499 87.2002 54.5699 87.2002 55.4499V71.4099Z"
|
||||
fill={ILLUSTRATION_COLOR_TOKEN_MAP.stroke.secondary}
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
import { InboxIllustration, SearchIllustration } from "./";
|
||||
|
||||
export const IllustrationMap = [
|
||||
{
|
||||
asset: <InboxIllustration className="w-20 h-20" />,
|
||||
title: "Inbox",
|
||||
},
|
||||
{
|
||||
asset: <SearchIllustration className="w-20 h-20" />,
|
||||
title: "Search",
|
||||
},
|
||||
];
|
||||
134
packages/propel/src/empty-state/assets/illustration/inbox.tsx
Normal file
134
packages/propel/src/empty-state/assets/illustration/inbox.tsx
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
import { type TIllustrationAssetProps, ILLUSTRATION_COLOR_TOKEN_MAP } from "../helper";
|
||||
|
||||
export const InboxIllustration = ({ className, ...rest }: TIllustrationAssetProps) => (
|
||||
<svg
|
||||
width="100"
|
||||
height="92"
|
||||
viewBox="0 0 100 92"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={className}
|
||||
{...rest}
|
||||
>
|
||||
<path
|
||||
d="M47.9704 90.9139C45.6095 90.9139 43.3811 90.4381 41.6888 89.5768L3.42089 70.0815C1.60205 69.1601 0.602295 67.8772 0.602295 66.48V46.5631C0.602295 45.1719 1.60205 43.8951 3.42089 42.9676L45.748 21.4005C47.4344 20.5393 49.6628 20.0635 52.0236 20.0635C54.3845 20.0635 56.6129 20.5393 58.2992 21.4005L96.5732 40.9018C98.392 41.8233 99.3917 43.1061 99.3917 44.5034V64.4202C99.3917 65.8115 98.392 67.0883 96.5732 68.0158L54.246 89.5829C52.5597 90.4441 50.3313 90.9199 47.9644 90.9199L47.9704 90.9139Z"
|
||||
fill={ILLUSTRATION_COLOR_TOKEN_MAP.fill.primary}
|
||||
/>
|
||||
<path
|
||||
d="M52.0296 20.6717C54.2038 20.6717 56.3719 21.0933 58.0282 21.9425L96.3021 41.4438C97.9643 42.287 98.7894 43.3952 98.7894 44.5094V64.4263C98.7894 65.5344 97.9583 66.6426 96.3021 67.4858L53.9749 89.0528C52.3187 89.902 50.1445 90.3236 47.9703 90.3236C45.7962 90.3236 43.622 89.902 41.9658 89.0528L3.69185 69.5455C2.0296 68.7023 1.2045 67.5942 1.2045 66.486V46.5691C1.2045 45.4609 2.03562 44.3588 3.69185 43.5096L46.019 21.9425C47.6752 21.0933 49.8494 20.6717 52.0236 20.6717M52.0296 19.4672C49.5724 19.4672 47.2476 19.9671 45.477 20.8705L3.14981 42.4316C1.12018 43.4674 -3.05176e-05 44.9309 -3.05176e-05 46.5631V66.48C-3.05176e-05 67.4255 0.409509 69.2263 3.14981 70.6175L41.4177 90.1189C43.1884 91.0223 45.5131 91.5221 47.9703 91.5221C50.4276 91.5221 52.7523 91.0223 54.523 90.1189L96.8501 68.5518C98.8798 67.5159 100 66.0524 100 64.4202V44.5033C100 43.5578 99.5904 41.757 96.8501 40.3658L58.5762 20.8645C56.8116 19.9611 54.4808 19.4612 52.0296 19.4612V19.4672Z"
|
||||
fill={ILLUSTRATION_COLOR_TOKEN_MAP.stroke.primary}
|
||||
/>
|
||||
<path
|
||||
d="M86.7922 46.4305C86.6838 46.4968 86.5754 46.563 86.455 46.6233L79.4085 50.2128L79.3844 50.2308L79.3964 50.2128L86.7982 46.4366L86.7922 46.4305Z"
|
||||
fill={ILLUSTRATION_COLOR_TOKEN_MAP.fill.primary}
|
||||
stroke={ILLUSTRATION_COLOR_TOKEN_MAP.stroke.secondary}
|
||||
strokeWidth="0.3024"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M76.6561 56.5847L76.114 57.8495L75.1504 60.0899C74.6806 61.186 73.9098 62.0894 73.0606 62.5231L64.3699 66.9557L62.6595 64.7033L56.8115 61.722H56.7934L51.7765 64.2756C49.0724 65.4862 44.9589 65.426 42.3812 64.113L28.2159 56.8919L47.8618 46.8883C50.5479 45.5211 54.9083 45.5211 57.5944 46.8883L71.7597 54.1094L76.644 56.5908L76.6561 56.5847Z"
|
||||
fill={ILLUSTRATION_COLOR_TOKEN_MAP.fill.primary}
|
||||
stroke={ILLUSTRATION_COLOR_TOKEN_MAP.stroke.secondary}
|
||||
strokeWidth="0.3024"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M88.4665 44.142C88.4665 44.9611 87.9064 45.7742 86.7922 46.4306L79.3904 50.2068L79.3783 50.2249L76.6561 56.5848L71.7717 54.1035L57.6065 46.8823C54.9204 45.5152 50.56 45.5152 47.8739 46.8823L28.228 56.886L13.5448 49.4058C12.2018 48.7193 11.5272 47.8219 11.5272 46.9245C11.5272 46.0271 12.2018 45.1298 13.5448 44.4432L47.8679 26.9594C50.554 25.5923 54.9144 25.5923 57.6005 26.9594L86.4489 41.6547C87.792 42.3413 88.4665 43.2386 88.4665 44.136V44.142Z"
|
||||
fill={ILLUSTRATION_COLOR_TOKEN_MAP.fill.secondary}
|
||||
stroke={ILLUSTRATION_COLOR_TOKEN_MAP.stroke.secondary}
|
||||
strokeWidth="0.3024"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M98.7954 44.5034C98.7954 45.5995 97.9763 46.6896 96.3562 47.5328L88.4666 51.5499L85.2505 53.1881L79.4025 50.2068L86.449 46.6173C86.5694 46.5571 86.6839 46.4969 86.7862 46.4246C87.9064 45.7742 88.4605 44.9551 88.4605 44.136C88.4605 43.2326 87.786 42.3352 86.4429 41.6547L57.5945 26.9594C54.9084 25.5923 50.548 25.5923 47.8619 26.9594L13.5388 44.4432C12.1958 45.1298 11.5212 46.0271 11.5212 46.9245C11.5212 47.8219 12.1958 48.7253 13.5388 49.4058L28.222 56.886L42.3873 64.1071C44.965 65.4261 49.0785 65.4803 51.7826 64.2697C51.9031 64.2155 52.0175 64.1613 52.1259 64.1071L56.8115 61.7161L62.6595 64.6973L53.9688 69.124C53.0956 69.5696 52.0838 69.9009 50.9997 70.1057C49.2832 70.4429 47.4042 70.4851 45.6576 70.2321C44.3025 70.0334 43.0257 69.672 41.9597 69.124L23.0787 59.5058L11.5212 53.6157L3.68578 49.6226C2.02353 48.7795 1.19843 47.6713 1.19843 46.5571C1.19843 45.4429 2.02955 44.3468 3.68578 43.4976L46.0129 21.9305C49.3314 20.2382 54.7096 20.2382 58.0161 21.9305L96.29 41.4319C97.9522 42.275 98.7773 43.3832 98.7773 44.4974L98.7954 44.5034Z"
|
||||
fill={ILLUSTRATION_COLOR_TOKEN_MAP.fill.primary}
|
||||
stroke={ILLUSTRATION_COLOR_TOKEN_MAP.stroke.secondary}
|
||||
strokeWidth="0.3024"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M86.7921 46.4305C86.6837 46.4968 86.5753 46.563 86.4549 46.6233L79.4084 50.2128H79.3903L86.7921 46.4366V46.4305Z"
|
||||
fill={ILLUSTRATION_COLOR_TOKEN_MAP.fill.primary}
|
||||
stroke={ILLUSTRATION_COLOR_TOKEN_MAP.stroke.secondary}
|
||||
strokeWidth="0.3024"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M85.2503 53.1939L82.5161 59.5719L81.0164 63.0771C80.5467 64.1732 79.7758 65.0766 78.9266 65.5102L68.9651 70.5873C68.3869 70.8824 67.8268 70.9126 67.3872 70.6837C67.2005 70.5934 67.0379 70.4548 66.8993 70.2742L64.3759 66.9497L73.0665 62.517C73.9157 62.0894 74.6866 61.18 75.1564 60.0838L76.12 57.8434L76.6621 56.5787L79.3843 50.2188L79.4084 50.2007L85.2564 53.1819L85.2503 53.1939Z"
|
||||
fill={ILLUSTRATION_COLOR_TOKEN_MAP.fill.primary}
|
||||
stroke={ILLUSTRATION_COLOR_TOKEN_MAP.stroke.secondary}
|
||||
strokeWidth="0.3024"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M79.4025 50.2067L79.3783 50.2249L79.3904 50.2007L79.4025 50.2067Z"
|
||||
fill={ILLUSTRATION_COLOR_TOKEN_MAP.fill.primary}
|
||||
stroke={ILLUSTRATION_COLOR_TOKEN_MAP.stroke.secondary}
|
||||
strokeWidth="0.3024"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M98.7953 44.5034V64.4203C98.7953 65.5285 97.9642 66.6366 96.308 67.4798L53.9808 89.0469C50.6624 90.7393 45.2841 90.7393 41.9717 89.0469L3.69176 69.5456C2.02951 68.7024 1.20441 67.5942 1.20441 66.4861V46.5692C1.20441 47.6774 2.02951 48.7855 3.69176 49.6347L11.5272 53.6277L23.0847 59.5179L41.9657 69.136C43.0317 69.6841 44.3145 70.0515 45.6636 70.2442C47.4162 70.4911 49.2952 70.449 51.0057 70.1177C52.0897 69.913 53.1015 69.5817 53.9748 69.136L62.6655 64.7094L64.3759 66.9619L66.8994 70.2864C67.0379 70.467 67.2006 70.6056 67.3873 70.6959C67.8269 70.9248 68.387 70.9007 68.9652 70.5995L78.9266 65.5224C79.7758 65.0948 80.5467 64.1854 81.0165 63.0893L82.5161 59.5841L85.2504 53.2061L88.4665 51.568L96.3562 47.5509C97.9763 46.7077 98.7953 45.6176 98.7953 44.5215V44.5034Z"
|
||||
fill={ILLUSTRATION_COLOR_TOKEN_MAP.fill.secondary}
|
||||
stroke={ILLUSTRATION_COLOR_TOKEN_MAP.stroke.secondary}
|
||||
strokeWidth="0.3024"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M56.8175 61.7161L52.1319 64.1071C52.0175 64.1673 51.9091 64.2215 51.7886 64.2697L56.8055 61.7101H56.8236L56.8175 61.7161Z"
|
||||
fill={ILLUSTRATION_COLOR_TOKEN_MAP.fill.primary}
|
||||
stroke={ILLUSTRATION_COLOR_TOKEN_MAP.stroke.secondary}
|
||||
strokeWidth="0.3024"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M43.333 50.7852C42.821 50.7852 42.3452 50.6707 41.9116 50.4539L36.5454 47.7196C35.3168 47.0933 34.6061 45.6719 34.6061 43.829V23.87C34.6061 23.87 34.6061 23.8338 34.6061 23.8097V22.027C34.6061 18.823 36.7622 15.1191 39.4062 13.77L63.2679 1.61027C63.9485 1.26096 64.623 1.0863 65.2674 1.0863C65.7794 1.0863 66.2551 1.20073 66.6828 1.41755L72.0489 4.15183C73.2775 4.77819 73.9822 6.19953 73.9822 8.04848V9.84323C73.9822 9.84323 73.9822 9.87937 73.9822 9.90346V29.8505C73.9822 33.0545 71.8261 36.7584 69.1821 38.1075L45.3204 50.2672C44.6399 50.6105 43.9714 50.7912 43.333 50.7912V50.7852Z"
|
||||
fill={ILLUSTRATION_COLOR_TOKEN_MAP.fill.primary}
|
||||
/>
|
||||
<path
|
||||
d="M65.2734 1.68846C65.6889 1.68846 66.0744 1.7788 66.4177 1.95345L71.7839 4.68773C72.7656 5.18761 73.3859 6.37408 73.3859 8.04837V9.84312C73.3859 9.84312 73.3859 9.86721 73.3859 9.87926V29.8383C73.3859 32.8316 71.3743 36.3066 68.9111 37.5593L45.0493 49.7191C44.4411 50.0262 43.8629 50.1768 43.3329 50.1768C42.9113 50.1768 42.5258 50.0864 42.1826 49.9118L36.8164 47.1775C35.8347 46.6776 35.2083 45.4972 35.2083 43.8229V23.8638C35.2083 23.8638 35.2083 23.8458 35.2083 23.8397V22.0269C35.2083 19.0337 37.2199 15.5586 39.6832 14.3059L63.5449 2.14618C64.1592 1.833 64.7374 1.68846 65.2674 1.68846M65.2734 0.483928C64.5326 0.483928 63.7737 0.682675 63.0028 1.07415L39.1411 13.2339C36.2623 14.6974 34.0098 18.5639 34.0098 22.033V23.7916C34.0098 23.7916 34.0098 23.8458 34.0098 23.8699V43.8289C34.0098 45.9067 34.8349 47.5208 36.2743 48.2556L41.6405 50.9898C42.1585 51.2548 42.7306 51.3873 43.3389 51.3873C44.0737 51.3873 44.8385 51.1886 45.6034 50.8031L69.4651 38.6434C72.344 37.1799 74.5964 33.3134 74.5964 29.8443V9.92141C74.5964 9.92141 74.5964 9.86721 74.5964 9.8371V8.04235C74.5964 5.95851 73.7713 4.34444 72.3319 3.60968L66.9657 0.8754C66.4478 0.610404 65.8817 0.477905 65.2734 0.477905V0.483928Z"
|
||||
fill={ILLUSTRATION_COLOR_TOKEN_MAP.stroke.primary}
|
||||
/>
|
||||
<path
|
||||
d="M72.85 8.76514C72.3682 8.51822 71.6575 8.80128 71.0854 9.53002L70.3988 10.4033L57.7392 26.6705C57.2755 27.2306 56.6973 27.5317 56.2938 27.4414L43.5559 24.0747L42.8693 23.9001C42.1707 23.7134 41.2432 24.4361 40.8095 25.49C40.6469 25.8815 40.5686 26.267 40.5686 26.5922V46.5512C40.5686 48.2195 41.195 49.406 42.1767 49.9058L36.8105 47.1716C35.8288 46.6717 35.2025 45.4912 35.2025 43.8169V23.8579C35.2025 23.5327 35.2747 23.1472 35.4434 22.7557C35.877 21.7018 36.7985 20.973 37.5031 21.1658L38.1897 21.3404L50.9276 24.7071C51.3311 24.7914 51.9093 24.4963 52.373 23.9362L65.0326 7.66902L65.7192 6.79574C66.2853 6.067 67.002 5.78393 67.4838 6.03086L72.85 8.76514Z"
|
||||
fill={ILLUSTRATION_COLOR_TOKEN_MAP.fill.secondary}
|
||||
stroke={ILLUSTRATION_COLOR_TOKEN_MAP.stroke.secondary}
|
||||
strokeWidth="0.3024"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M72.8499 8.76507C72.9644 8.8253 73.0607 8.90962 73.145 9.02405C73.3077 9.24689 73.386 9.55404 73.386 9.88529V29.8443C73.386 32.8376 71.3744 36.3127 68.9111 37.5654L45.0494 49.7251C43.9593 50.2792 42.9596 50.3093 42.1826 49.9118C41.2009 49.4119 40.5746 48.2315 40.5746 46.5572V26.5981C40.5746 26.2729 40.6469 25.8875 40.8155 25.496C41.2491 24.442 42.1706 23.7133 42.8752 23.906L43.5618 24.0807L56.2997 27.4473C56.7032 27.5316 57.2814 27.2365 57.7451 26.6764L70.4047 10.4093L71.0913 9.53597C71.6575 8.80723 72.3741 8.52417 72.856 8.7711L72.8499 8.76507Z"
|
||||
fill={ILLUSTRATION_COLOR_TOKEN_MAP.fill.primary}
|
||||
stroke={ILLUSTRATION_COLOR_TOKEN_MAP.stroke.secondary}
|
||||
strokeWidth="0.3024"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M71.784 4.68781C71.007 4.29031 70.0073 4.32645 68.9172 4.88053L45.0554 17.0403C42.5922 18.293 40.5806 21.768 40.5806 24.7613V26.5741C40.5806 27.092 40.7673 27.5016 41.1106 27.6762L35.7444 24.942C35.4011 24.7673 35.2144 24.3578 35.2144 23.8398V22.027C35.2144 19.0337 37.226 15.5587 39.6893 14.306L63.551 2.14625C64.6411 1.59217 65.6409 1.55603 66.4178 1.95352L71.784 4.68781Z"
|
||||
fill={ILLUSTRATION_COLOR_TOKEN_MAP.fill.secondary}
|
||||
stroke={ILLUSTRATION_COLOR_TOKEN_MAP.stroke.secondary}
|
||||
strokeWidth="0.3024"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M71.784 4.68778C72.7656 5.18766 73.386 6.37412 73.386 8.04239V9.83714C73.386 10.4514 73.1451 11.1561 72.6994 11.7282L70.4048 14.6793L59.293 28.959C58.5642 29.8383 57.7693 30.4948 56.9803 30.8983C56.1913 31.3018 55.4264 31.4524 54.7459 31.3139L43.5618 28.3387L41.2793 27.7425C41.219 27.7244 41.1648 27.7063 41.1106 27.6762C40.7673 27.5016 40.5806 27.092 40.5806 26.5741V24.7613C40.5806 21.768 42.5922 18.2929 45.0555 17.0402L68.9172 4.8805C70.0073 4.32642 71.007 4.29028 71.784 4.68778Z"
|
||||
fill={ILLUSTRATION_COLOR_TOKEN_MAP.fill.primary}
|
||||
stroke={ILLUSTRATION_COLOR_TOKEN_MAP.stroke.secondary}
|
||||
strokeWidth="0.3024"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
export * from "./inbox";
|
||||
export * from "./search";
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
import { type TIllustrationAssetProps, ILLUSTRATION_COLOR_TOKEN_MAP } from "../helper";
|
||||
|
||||
export const SearchIllustration = ({ className, ...rest }: TIllustrationAssetProps) => (
|
||||
<svg
|
||||
width="161"
|
||||
height="168"
|
||||
viewBox="0 0 161 168"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={className}
|
||||
{...rest}
|
||||
>
|
||||
<path
|
||||
d="M144.84 4.63218C142.624 3.50452 139.768 3.59686 136.656 5.18614L20.4472 64.3983C13.4043 67.9857 7.69345 77.8445 7.69345 86.4108V156.537C7.69345 161.318 9.46737 164.688 12.2569 166.106L5.5634 162.696C2.77391 161.272 1 157.902 1 153.128V83.0015C1 74.4286 6.71087 64.5764 13.7538 60.989L129.962 1.77674C133.082 0.187462 135.93 0.0951613 138.146 1.22282L144.84 4.63218Z"
|
||||
fill={ILLUSTRATION_COLOR_TOKEN_MAP.fill.secondary}
|
||||
stroke={ILLUSTRATION_COLOR_TOKEN_MAP.stroke.secondary}
|
||||
strokeWidth="0.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M144.84 4.6322C147.629 6.05662 149.403 9.42642 149.403 14.2009V84.327C149.403 92.8998 143.692 102.752 136.649 106.339L20.4407 165.552C17.3215 167.141 14.4726 167.233 12.2569 166.106C9.4674 164.681 7.69348 161.311 7.69348 156.537V86.4109C7.69348 77.838 13.4043 67.9858 20.4473 64.3983L136.656 5.18616C139.775 3.59688 142.624 3.50454 144.84 4.6322Z"
|
||||
fill={ILLUSTRATION_COLOR_TOKEN_MAP.fill.primary}
|
||||
stroke={ILLUSTRATION_COLOR_TOKEN_MAP.stroke.secondary}
|
||||
strokeWidth="0.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M151.843 141.244C151.019 141.244 150.28 141.013 149.641 140.558L147.108 138.771C146.772 138.844 146.436 138.883 146.112 138.883C145.295 138.883 144.55 138.653 143.91 138.204L121.119 122.081C113.252 131.399 103.611 136.945 95.2226 136.945C91.7803 136.945 88.661 136.035 85.9441 134.241C83.702 133.792 81.6247 132.902 79.7782 131.59C68.1521 123.366 68.0663 100.49 79.587 80.5944C88.0939 65.9018 100.821 56.4123 111.999 56.4123C115.448 56.4123 118.574 57.3224 121.291 59.1227C123.539 59.5777 125.61 60.4679 127.457 61.7671C137.764 69.0606 139.215 87.6901 130.978 106.221L158.418 125.628C160.944 127.415 161.029 132.091 158.609 136.265C157.475 138.224 155.958 139.76 154.329 140.585C153.479 141.02 152.641 141.238 151.837 141.238L151.843 141.244ZM90.8636 123.657C91.3252 123.709 91.8066 123.736 92.2814 123.736C100.287 123.736 109.467 116.818 115.666 106.115C123.777 92.115 124.074 75.8332 116.371 69.6937C115.91 69.6409 115.428 69.6145 114.947 69.6145C106.941 69.6145 97.7615 76.5322 91.5626 87.2285C83.4514 101.229 83.1546 117.511 90.857 123.65L90.8636 123.657Z"
|
||||
fill={ILLUSTRATION_COLOR_TOKEN_MAP.fill.primary}
|
||||
/>
|
||||
<path
|
||||
d="M112.006 57.0718C115.277 57.0718 118.35 57.9291 121.04 59.7425C123.197 60.158 125.234 61.0021 127.081 62.3078C137.309 69.542 138.344 88.4287 130.16 106.452L158.049 126.176C160.271 127.745 160.271 132.111 158.049 135.942C156.941 137.855 155.49 139.266 154.039 140.004C153.294 140.387 152.549 140.585 151.843 140.585C151.184 140.585 150.564 140.407 150.023 140.024L147.247 138.059C146.864 138.165 146.482 138.224 146.112 138.224C145.453 138.224 144.833 138.046 144.292 137.663L121.007 121.19C113.107 130.752 103.525 136.285 95.2292 136.285C91.9649 136.285 88.8985 135.428 86.2079 133.621C84.0449 133.205 82.0138 132.361 80.1673 131.056C68.7917 123.01 68.7917 100.569 80.1673 80.9307C88.7797 66.0667 101.435 57.0784 112.012 57.0784M92.288 124.395C100.241 124.395 109.764 117.629 116.246 106.445C124.667 91.9106 124.792 75.3452 116.642 69.0606C116.088 68.9881 115.527 68.9551 114.96 68.9551C107.007 68.9551 97.4845 75.7211 91.0087 86.8988C82.5875 101.433 82.4622 117.999 90.613 124.283C91.167 124.356 91.7275 124.389 92.2946 124.389M112.006 55.7529C100.591 55.7529 87.6455 65.3743 79.0198 80.2647C73.4013 89.9586 70.3085 100.642 70.3085 110.336C70.3085 120.241 73.5332 127.983 79.3957 132.131C81.2884 133.469 83.4052 134.386 85.6869 134.861C88.483 136.681 91.6879 137.604 95.216 137.604C103.65 137.604 113.305 132.15 121.218 122.964L143.514 138.738C144.259 139.266 145.15 139.543 146.093 139.543C146.37 139.543 146.653 139.516 146.937 139.47L149.238 141.099C149.983 141.627 150.874 141.904 151.823 141.904C152.773 141.904 153.67 141.66 154.613 141.178C156.354 140.295 157.969 138.666 159.163 136.602C161.755 132.131 161.583 127.079 158.781 125.094L131.776 105.997C139.894 87.3472 138.285 68.6386 127.813 61.2329C125.933 59.9008 123.816 58.9842 121.522 58.5028C118.719 56.6827 115.507 55.7529 111.973 55.7529H112.006ZM91.1274 123.024C83.8206 116.983 84.2361 101.202 92.1364 87.5648C98.1242 77.2246 107.297 70.274 114.947 70.274C115.336 70.274 115.725 70.2938 116.107 70.3267C123.414 76.3607 122.992 92.1414 115.092 105.785C109.104 116.126 99.9311 123.076 92.2748 123.076C91.8857 123.076 91.4967 123.057 91.1208 123.024H91.1274Z"
|
||||
fill={ILLUSTRATION_COLOR_TOKEN_MAP.stroke.primary}
|
||||
/>
|
||||
<path
|
||||
d="M152.311 123.815L124.423 104.091C132.607 86.0679 131.572 67.1877 121.343 59.947C109.968 51.9016 91.5296 61.2922 80.1541 80.9307C68.7785 100.569 68.7785 123.01 80.1541 131.056C89.914 137.96 104.897 132.012 116.134 117.755L144.279 137.663C145.387 138.448 146.844 138.382 148.295 137.644C149.746 136.905 151.197 135.487 152.305 133.581C154.527 129.75 154.527 125.384 152.305 123.815H152.311ZM85.2715 122.239C76.7184 116.185 76.7184 99.3162 85.2715 84.5445C93.8245 69.7794 107.693 62.7101 116.246 68.7638C124.799 74.811 124.799 91.6864 116.246 106.452C107.693 121.217 93.8245 128.286 85.2781 122.239H85.2715Z"
|
||||
fill={ILLUSTRATION_COLOR_TOKEN_MAP.fill.secondary}
|
||||
stroke={ILLUSTRATION_COLOR_TOKEN_MAP.stroke.secondary}
|
||||
strokeWidth="0.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M158.042 126.176L130.154 106.452C138.338 88.4287 137.302 69.5486 127.074 62.3078C115.699 54.2625 97.2603 63.6531 85.8847 83.2916C74.5092 102.93 74.5092 125.371 85.8847 133.416C95.6446 140.321 110.627 134.373 121.864 120.115L150.01 140.024C151.118 140.809 152.575 140.743 154.026 140.004C155.477 139.266 156.928 137.848 158.035 135.942C160.258 132.111 160.258 127.745 158.035 126.176H158.042ZM91.0021 124.6C82.449 118.546 82.449 101.677 91.0021 86.9053C99.5552 72.1402 113.423 65.0709 121.977 71.1247C130.53 77.1718 130.53 94.0472 121.977 108.812C113.423 123.577 99.5552 130.647 91.0087 124.6H91.0021Z"
|
||||
fill={ILLUSTRATION_COLOR_TOKEN_MAP.fill.primary}
|
||||
stroke={ILLUSTRATION_COLOR_TOKEN_MAP.stroke.secondary}
|
||||
strokeWidth="0.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M37.1248 108.984C28.6904 113.283 21.8256 108.43 21.8256 98.1622C21.8256 87.8945 28.6904 76.0441 37.1248 71.7445C45.5592 67.4449 52.4241 72.2984 52.4241 82.5661C52.4241 92.8338 45.5592 104.684 37.1248 108.984ZM37.1248 74.6065C29.9829 78.2467 24.1798 88.2704 24.1798 96.962C24.1798 105.654 29.9895 109.755 37.1248 106.122C44.2667 102.482 50.0699 92.4579 50.0699 83.7663C50.0699 75.0813 44.2601 70.9729 37.1248 74.6065ZM37.138 101.829C36.4852 102.158 35.951 101.796 35.951 101.004C35.951 100.213 36.4786 99.2898 37.1248 98.9601H37.138C37.7909 98.6238 38.3119 98.9931 38.3119 99.7844C38.3119 100.576 37.7843 101.499 37.138 101.829ZM37.1248 96.0915C36.472 96.4212 35.951 96.0519 35.951 95.2606V93.8296C35.951 91.9237 37.0523 89.5761 38.8922 87.5516C39.1889 87.2284 39.4527 86.8855 39.6769 86.5228C40.31 85.5205 40.6595 84.4719 40.6595 83.5487C40.6595 82.6255 40.31 81.9264 39.6835 81.5769C38.2986 80.8054 35.9576 81.999 34.5793 84.1818C34.0913 84.9467 33.3462 85.2699 32.9175 84.894C32.4889 84.5181 32.5416 83.5883 33.0296 82.8167C35.2982 79.2557 38.9713 77.3763 41.2398 78.6358C42.3873 79.2755 43.0203 80.5943 43.0203 82.3551C43.0203 84.1158 42.3873 86.0744 41.2398 87.8813C40.8573 88.488 40.4155 89.0749 39.9275 89.6091C39.1493 90.4663 38.3185 91.7061 38.3185 92.6294V94.0604C38.3185 94.8517 37.7909 95.7618 37.1446 96.0915H37.1248Z"
|
||||
fill={ILLUSTRATION_COLOR_TOKEN_MAP.stroke.secondary}
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
|
@ -1,3 +1,6 @@
|
|||
export * from "./horizontal-stack";
|
||||
export * from "./vertical-stack";
|
||||
export * from "./asset-registry";
|
||||
export * from "./asset-types";
|
||||
export * from "./helper";
|
||||
export * from "./horizontal-stack";
|
||||
export * from "./illustration";
|
||||
export * from "./vertical-stack";
|
||||
|
|
|
|||
142
packages/propel/src/empty-state/compact-empty-state.stories.tsx
Normal file
142
packages/propel/src/empty-state/compact-empty-state.stories.tsx
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import { EmptyStateCompact } from "./compact-empty-state";
|
||||
import type { BaseEmptyStateCommonProps } from "./types";
|
||||
|
||||
const meta: Meta<BaseEmptyStateCommonProps> = {
|
||||
title: "Components/EmptyState/Compact",
|
||||
component: EmptyStateCompact,
|
||||
parameters: {
|
||||
layout: "centered",
|
||||
docs: {
|
||||
description: {
|
||||
component:
|
||||
"A compact empty state component with centered title, asset, and action buttons. Best used for simple, space-constrained empty states. Supports horizontal stack and illustration assets via `assetKey`.",
|
||||
},
|
||||
},
|
||||
},
|
||||
argTypes: {
|
||||
title: {
|
||||
control: "text",
|
||||
description: "The main title text for the empty state",
|
||||
},
|
||||
assetKey: {
|
||||
control: "select",
|
||||
options: [
|
||||
"customer",
|
||||
"epic",
|
||||
"estimate",
|
||||
"export",
|
||||
"intake",
|
||||
"label",
|
||||
"link",
|
||||
"members",
|
||||
"note",
|
||||
"priority",
|
||||
"project",
|
||||
"settings",
|
||||
"state",
|
||||
"template",
|
||||
"token",
|
||||
"unknown",
|
||||
"update",
|
||||
"webhook",
|
||||
"work-item",
|
||||
"worklog",
|
||||
"inbox",
|
||||
],
|
||||
description: "Predefined asset key (horizontal-stack or illustration)",
|
||||
},
|
||||
className: {
|
||||
control: "text",
|
||||
description: "Additional CSS classes to apply to the content wrapper",
|
||||
},
|
||||
rootClassName: {
|
||||
control: "text",
|
||||
description: "Additional CSS classes to apply to the root container",
|
||||
},
|
||||
assetClassName: {
|
||||
control: "text",
|
||||
description: "Additional CSS classes to apply to the asset",
|
||||
},
|
||||
asset: {
|
||||
control: false,
|
||||
description: "Custom React node to display as the visual asset (use this for full control instead of assetKey)",
|
||||
},
|
||||
actions: {
|
||||
control: false,
|
||||
description: "Array of action buttons to display",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<BaseEmptyStateCommonProps>;
|
||||
|
||||
// Using assetKey (recommended approach)
|
||||
export const WithAssetKey: Story = {
|
||||
args: {
|
||||
assetKey: "work-item",
|
||||
assetClassName: "size-20",
|
||||
title: "There're no progress metrics to show yet.",
|
||||
},
|
||||
};
|
||||
|
||||
export const WithAssetKeyAndAction: Story = {
|
||||
args: {
|
||||
assetKey: "project",
|
||||
assetClassName: "size-20",
|
||||
title: "No projects found",
|
||||
actions: [
|
||||
{
|
||||
label: "Create Project",
|
||||
onClick: () => console.log("create-clicked"),
|
||||
variant: "primary",
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
export const WithAssetKeyAndMultipleActions: Story = {
|
||||
args: {
|
||||
assetKey: "members",
|
||||
assetClassName: "size-20",
|
||||
title: "Get started with your workspace",
|
||||
actions: [
|
||||
{
|
||||
label: "Create Project",
|
||||
onClick: () => console.log("create-clicked"),
|
||||
variant: "primary",
|
||||
},
|
||||
{
|
||||
label: "Import",
|
||||
onClick: () => console.log("import-clicked"),
|
||||
variant: "outline-primary",
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
// Using custom asset (legacy approach)
|
||||
export const WithCustomAsset: Story = {
|
||||
args: {
|
||||
asset: (
|
||||
<svg className="h-40 w-40" viewBox="0 0 160 180" fill="none">
|
||||
<rect width="160" height="180" fill="#F3F4F6" rx="8" />
|
||||
</svg>
|
||||
),
|
||||
title: "No items found",
|
||||
actions: [
|
||||
{
|
||||
label: "Create Item",
|
||||
onClick: () => console.log("create-clicked"),
|
||||
variant: "primary",
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
export const TitleOnly: Story = {
|
||||
args: {
|
||||
title: "No results found",
|
||||
},
|
||||
};
|
||||
60
packages/propel/src/empty-state/compact-empty-state.tsx
Normal file
60
packages/propel/src/empty-state/compact-empty-state.tsx
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
import React from "react";
|
||||
import { Button } from "../button/button";
|
||||
import { cn } from "../utils/classname";
|
||||
import { getCompactAsset } from "./assets/asset-registry";
|
||||
import type { CompactAssetType } from "./assets/asset-types";
|
||||
import type { BaseEmptyStateCommonProps } from "./types";
|
||||
|
||||
export const EmptyStateCompact: React.FC<BaseEmptyStateCommonProps> = ({
|
||||
asset,
|
||||
assetKey,
|
||||
title,
|
||||
description,
|
||||
actions,
|
||||
className,
|
||||
rootClassName,
|
||||
assetClassName,
|
||||
align = "center",
|
||||
}) => {
|
||||
// Determine which asset to use: assetKey takes precedence, fallback to custom asset
|
||||
const resolvedAsset = assetKey ? getCompactAsset(assetKey as CompactAssetType, assetClassName) : asset;
|
||||
|
||||
const rootAlignClasses = align === "center" ? "items-center" : "items-start";
|
||||
const containerAlignClasses = align === "center" ? "items-center text-center" : "items-start text-left";
|
||||
|
||||
return (
|
||||
<div className={cn("flex size-full items-center justify-center", rootAlignClasses, rootClassName)}>
|
||||
<div
|
||||
className={cn("flex max-w-[25rem] size-full flex-col justify-center gap-3", containerAlignClasses, className)}
|
||||
>
|
||||
{resolvedAsset && <div className="flex max-w-40 items-center">{resolvedAsset}</div>}
|
||||
|
||||
<div className="flex flex-col gap-4">
|
||||
{title && description ? (
|
||||
<div className="flex flex-col gap-2">
|
||||
{title && <h3 className="text-lg font-semibold leading-7 text-custom-text-100">{title}</h3>}
|
||||
{description && <p className="text-sm leading-5 text-custom-text-300">{description}</p>}
|
||||
</div>
|
||||
) : (
|
||||
title && <p className="text-sm leading-5 text-custom-text-300">{title}</p>
|
||||
)}
|
||||
|
||||
{actions && actions.length > 0 && (
|
||||
<div className="flex flex-col gap-4 sm:flex-row">
|
||||
{actions.map((action, index) => {
|
||||
const { label, variant, ...rest } = action;
|
||||
return (
|
||||
<Button key={index} variant={variant} {...rest}>
|
||||
{label}
|
||||
</Button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
EmptyStateCompact.displayName = "EmptyStateCompact";
|
||||
295
packages/propel/src/empty-state/detailed-empty-state.stories.tsx
Normal file
295
packages/propel/src/empty-state/detailed-empty-state.stories.tsx
Normal file
|
|
@ -0,0 +1,295 @@
|
|||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import { EmptyStateDetailed } from "./detailed-empty-state";
|
||||
import type { BaseEmptyStateCommonProps } from "./types";
|
||||
|
||||
const meta: Meta<BaseEmptyStateCommonProps> = {
|
||||
title: "Components/EmptyState/Detailed",
|
||||
component: EmptyStateDetailed,
|
||||
parameters: {
|
||||
layout: "centered",
|
||||
docs: {
|
||||
description: {
|
||||
component:
|
||||
"A detailed empty state component with title, description, asset, and action buttons. Best used for feature-specific empty states that need more context. Supports vertical stack and illustration assets via `assetKey`.",
|
||||
},
|
||||
},
|
||||
},
|
||||
argTypes: {
|
||||
title: {
|
||||
control: "text",
|
||||
description: "The main title text for the empty state",
|
||||
},
|
||||
description: {
|
||||
control: "text",
|
||||
description: "Optional description text that appears below the title",
|
||||
},
|
||||
assetKey: {
|
||||
control: "select",
|
||||
options: [
|
||||
"archived-cycle",
|
||||
"archived-module",
|
||||
"archived-work-item",
|
||||
"customer",
|
||||
"cycle",
|
||||
"dashboard",
|
||||
"draft",
|
||||
"epic",
|
||||
"error-404",
|
||||
"invalid-link",
|
||||
"module",
|
||||
"no-access",
|
||||
"page",
|
||||
"project",
|
||||
"server-error",
|
||||
"teamspace",
|
||||
"view",
|
||||
"work-item",
|
||||
"inbox",
|
||||
],
|
||||
description: "Predefined asset key (vertical-stack or illustration)",
|
||||
},
|
||||
className: {
|
||||
control: "text",
|
||||
description: "Additional CSS classes to apply to the content wrapper",
|
||||
},
|
||||
rootClassName: {
|
||||
control: "text",
|
||||
description: "Additional CSS classes to apply to the root container",
|
||||
},
|
||||
assetClassName: {
|
||||
control: "text",
|
||||
description: "Additional CSS classes to apply to the asset",
|
||||
},
|
||||
asset: {
|
||||
control: false,
|
||||
description: "Custom React node to display as the visual asset (use this for full control instead of assetKey)",
|
||||
},
|
||||
actions: {
|
||||
control: false,
|
||||
description: "Array of action buttons to display",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<BaseEmptyStateCommonProps>;
|
||||
|
||||
// Primary story - showcases the most common usage
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
assetKey: "epic",
|
||||
assetClassName: "w-40 h-45",
|
||||
title: "Create an epic and split work into smaller goals",
|
||||
description: "For larger bodies of work that span several cycles and can live across modules, create an epic.",
|
||||
actions: [
|
||||
{
|
||||
label: "Create an Epic",
|
||||
onClick: () => console.log("primary-action-clicked"),
|
||||
variant: "primary",
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
export const WithSingleAction: Story = {
|
||||
args: {
|
||||
assetKey: "project",
|
||||
assetClassName: "w-40 h-45",
|
||||
title: "No projects found",
|
||||
description: "Get started by creating your first project to organize your work.",
|
||||
actions: [
|
||||
{
|
||||
label: "Create Project",
|
||||
onClick: () => console.log("create-clicked"),
|
||||
variant: "primary",
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
export const WithMultipleActions: Story = {
|
||||
args: {
|
||||
assetKey: "module",
|
||||
assetClassName: "w-40 h-45",
|
||||
title: "No modules found",
|
||||
description: "Get started by creating your first module or import existing ones.",
|
||||
actions: [
|
||||
{
|
||||
label: "Create Module",
|
||||
onClick: () => console.log("create-clicked"),
|
||||
variant: "primary",
|
||||
},
|
||||
{
|
||||
label: "Import Modules",
|
||||
onClick: () => console.log("import-clicked"),
|
||||
variant: "outline-primary",
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
export const WithoutActions: Story = {
|
||||
args: {
|
||||
assetKey: "dashboard",
|
||||
assetClassName: "w-40 h-45",
|
||||
title: "No activity yet",
|
||||
description: "Your activity feed will show up here once you start using the platform.",
|
||||
},
|
||||
};
|
||||
|
||||
export const ErrorState: Story = {
|
||||
args: {
|
||||
assetKey: "error-404",
|
||||
assetClassName: "w-40 h-45",
|
||||
title: "Page not found",
|
||||
description: "The page you're looking for doesn't exist or has been moved.",
|
||||
actions: [
|
||||
{
|
||||
label: "Go to Home",
|
||||
onClick: () => console.log("home-clicked"),
|
||||
variant: "primary",
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
export const ServerErrorState: Story = {
|
||||
name: "Error - Server",
|
||||
args: {
|
||||
assetKey: "server-error",
|
||||
assetClassName: "w-40 h-45",
|
||||
title: "Something went wrong",
|
||||
description: "We're experiencing technical difficulties. Please try again later.",
|
||||
actions: [
|
||||
{
|
||||
label: "Retry",
|
||||
onClick: () => console.log("retry-clicked"),
|
||||
variant: "primary",
|
||||
},
|
||||
{
|
||||
label: "Contact Support",
|
||||
onClick: () => console.log("support-clicked"),
|
||||
variant: "outline-primary",
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
export const NoAccessState: Story = {
|
||||
name: "Access Denied",
|
||||
args: {
|
||||
assetKey: "no-access",
|
||||
assetClassName: "w-40 h-45",
|
||||
title: "You don't have access",
|
||||
description: "Contact your workspace admin to request access to this resource.",
|
||||
},
|
||||
};
|
||||
|
||||
export const ArchivedState: Story = {
|
||||
name: "Archived Content",
|
||||
args: {
|
||||
assetKey: "archived-work-item",
|
||||
assetClassName: "w-40 h-45",
|
||||
title: "No archived items",
|
||||
description: "Archived items will appear here when you archive them.",
|
||||
},
|
||||
};
|
||||
|
||||
export const CycleState: Story = {
|
||||
name: "Cycles",
|
||||
args: {
|
||||
assetKey: "cycle",
|
||||
assetClassName: "w-40 h-45",
|
||||
title: "No cycles found",
|
||||
description: "Create cycles to organize your work into time-boxed iterations.",
|
||||
actions: [
|
||||
{
|
||||
label: "Create Cycle",
|
||||
onClick: () => console.log("create-cycle-clicked"),
|
||||
variant: "primary",
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
export const ModuleState: Story = {
|
||||
name: "Modules",
|
||||
args: {
|
||||
assetKey: "module",
|
||||
assetClassName: "w-40 h-45",
|
||||
title: "No modules found",
|
||||
description: "Modules help you organize related work items into logical groups.",
|
||||
actions: [
|
||||
{
|
||||
label: "Create Module",
|
||||
onClick: () => console.log("create-module-clicked"),
|
||||
variant: "primary",
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
export const ViewState: Story = {
|
||||
name: "Views",
|
||||
args: {
|
||||
assetKey: "view",
|
||||
assetClassName: "w-40 h-45",
|
||||
title: "No saved views",
|
||||
description: "Create custom views to filter and organize your work items.",
|
||||
actions: [
|
||||
{
|
||||
label: "Create View",
|
||||
onClick: () => console.log("create-view-clicked"),
|
||||
variant: "primary",
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
export const PageState: Story = {
|
||||
name: "Pages",
|
||||
args: {
|
||||
assetKey: "page",
|
||||
assetClassName: "w-40 h-45",
|
||||
title: "No pages found",
|
||||
description: "Create pages to document your project, share knowledge, and collaborate.",
|
||||
actions: [
|
||||
{
|
||||
label: "Create Page",
|
||||
onClick: () => console.log("create-page-clicked"),
|
||||
variant: "primary",
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
// Using custom asset (for special cases)
|
||||
export const WithCustomAsset: Story = {
|
||||
name: "Custom Asset",
|
||||
args: {
|
||||
asset: (
|
||||
<svg className="h-45 w-40" viewBox="0 0 160 180" fill="none">
|
||||
<rect width="160" height="180" fill="#F3F4F6" rx="8" />
|
||||
<circle cx="80" cy="90" r="30" fill="#E5E7EB" />
|
||||
</svg>
|
||||
),
|
||||
title: "Custom asset example",
|
||||
description: "This example uses a custom SVG asset instead of predefined assetKey.",
|
||||
actions: [
|
||||
{
|
||||
label: "Get Started",
|
||||
onClick: () => console.log("action-clicked"),
|
||||
variant: "primary",
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
// Minimal example
|
||||
export const Minimal: Story = {
|
||||
name: "Minimal - Text Only",
|
||||
args: {
|
||||
title: "No data available",
|
||||
description: "Data will appear here once available.",
|
||||
},
|
||||
};
|
||||
52
packages/propel/src/empty-state/detailed-empty-state.tsx
Normal file
52
packages/propel/src/empty-state/detailed-empty-state.tsx
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
import React from "react";
|
||||
import { Button } from "../button/button";
|
||||
import { cn } from "../utils/classname";
|
||||
import { getDetailedAsset } from "./assets/asset-registry";
|
||||
import type { DetailedAssetType } from "./assets/asset-types";
|
||||
import type { BaseEmptyStateCommonProps } from "./types";
|
||||
|
||||
export const EmptyStateDetailed: React.FC<BaseEmptyStateCommonProps> = ({
|
||||
asset,
|
||||
assetKey,
|
||||
title,
|
||||
description,
|
||||
actions,
|
||||
className,
|
||||
rootClassName,
|
||||
assetClassName,
|
||||
}) => {
|
||||
// Determine which asset to use: assetKey takes precedence, fallback to custom asset
|
||||
const resolvedAsset = assetKey ? getDetailedAsset(assetKey as DetailedAssetType, assetClassName) : asset;
|
||||
|
||||
return (
|
||||
<div className={cn("flex size-full items-center justify-center", rootClassName)}>
|
||||
<div className={cn("flex max-w-[25rem] size-full flex-col justify-center gap-6 text-left", className)}>
|
||||
{resolvedAsset && <div className="flex max-w-40 items-center">{resolvedAsset}</div>}
|
||||
|
||||
<div className="flex flex-col gap-4">
|
||||
{(title || description) && (
|
||||
<div className="flex flex-col gap-2">
|
||||
{title && <h3 className="text-lg font-semibold leading-7 text-custom-text-100">{title}</h3>}
|
||||
{description && <p className="text-sm leading-5 text-custom-text-300">{description}</p>}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{actions && actions.length > 0 && (
|
||||
<div className="flex flex-col gap-4 sm:flex-row">
|
||||
{actions.map((action, index) => {
|
||||
const { label, variant, ...rest } = action;
|
||||
return (
|
||||
<Button key={index} variant={variant} {...rest}>
|
||||
{label}
|
||||
</Button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
EmptyStateDetailed.displayName = "EmptyStateDetailed";
|
||||
|
|
@ -1,100 +0,0 @@
|
|||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import { WorkItemHorizontalStackIllustration } from "./assets/horizontal-stack";
|
||||
import { HorizontalStackAssetsMap } from "./assets/horizontal-stack/constant";
|
||||
import { WorkItemVerticalStackIllustration } from "./assets/vertical-stack";
|
||||
import { VerticalStackAssetsMap } from "./assets/vertical-stack/constant";
|
||||
import { EmptyState, type EmptyStateProps } from "./empty-state";
|
||||
|
||||
const meta: Meta<EmptyStateProps> = {
|
||||
title: "Components/EmptyState",
|
||||
component: EmptyState,
|
||||
parameters: {
|
||||
layout: "centered",
|
||||
docs: {
|
||||
description: {
|
||||
component:
|
||||
"A flexible empty state component that can display an asset, title, description, and action buttons.",
|
||||
},
|
||||
},
|
||||
},
|
||||
argTypes: {
|
||||
title: {
|
||||
control: "text",
|
||||
description: "The main title text for the empty state",
|
||||
},
|
||||
description: {
|
||||
control: "text",
|
||||
description: "Optional description text that appears below the title",
|
||||
},
|
||||
className: {
|
||||
control: "text",
|
||||
description: "Additional CSS classes to apply to the root element",
|
||||
},
|
||||
type: {
|
||||
control: "select",
|
||||
options: ["detailed", "simple"],
|
||||
description: "The layout type of the empty state",
|
||||
},
|
||||
asset: {
|
||||
control: false,
|
||||
description: "React node to display as the visual asset (icon, illustration, etc.)",
|
||||
},
|
||||
actions: {
|
||||
control: false,
|
||||
description: "Array of action buttons to display",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<EmptyStateProps>;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
asset: <WorkItemVerticalStackIllustration className="w-40 h-45" />,
|
||||
title: "Create an epic and split work into smaller goals",
|
||||
description: "For larger bodies of work that span several cycles and can live across modules, create an epic.",
|
||||
actions: [
|
||||
{
|
||||
label: "Create an Epic",
|
||||
onClick: () => console.log("primary-action-clicked"),
|
||||
variant: "primary",
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
export const Simple: Story = {
|
||||
args: {
|
||||
asset: <WorkItemHorizontalStackIllustration className="w-40 h-45" />,
|
||||
title: "There're no progress metrics to show yet.",
|
||||
description: "For larger bodies of work that span several cycles and can live across modules, create an epic.",
|
||||
type: "simple",
|
||||
},
|
||||
};
|
||||
|
||||
export const HorizontalStackAssets: Story = {
|
||||
render: () => (
|
||||
<div className="grid grid-cols-12 gap-6 w-full">
|
||||
{HorizontalStackAssetsMap.map((item) => (
|
||||
<div key={item.title} className="flex flex-col items-center justify-center gap-3 p-4 col-span-3">
|
||||
{item.asset}
|
||||
<p className="text-sm text-custom-text-200 text-right">{item.title}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
),
|
||||
};
|
||||
|
||||
export const VerticalStackAssets: Story = {
|
||||
render: () => (
|
||||
<div className="grid grid-cols-12 gap-6 w-full py-20">
|
||||
{VerticalStackAssetsMap.map((item) => (
|
||||
<div key={item.title} className="flex flex-col items-center justify-center gap-3 p-4 col-span-3">
|
||||
{item.asset}
|
||||
<p className="text-sm text-custom-text-200">{item.title}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
),
|
||||
};
|
||||
|
|
@ -1,62 +1,71 @@
|
|||
import React from "react";
|
||||
import { Button } from "../button/button";
|
||||
import { TButtonVariant } from "../button/helper";
|
||||
import { CompactAssetType, DetailedAssetType } from "./assets/asset-types";
|
||||
import { EmptyStateCompact } from "./compact-empty-state";
|
||||
import { EmptyStateDetailed } from "./detailed-empty-state";
|
||||
import type { BaseEmptyStateCommonProps } from "./types";
|
||||
|
||||
export interface ActionButton {
|
||||
label: string;
|
||||
onClick: () => void;
|
||||
variant?: TButtonVariant;
|
||||
disabled?: boolean;
|
||||
}
|
||||
/**
|
||||
* @deprecated Use EmptyStateCompact or EmptyStateDetailed directly with assetKey for better type safety
|
||||
*
|
||||
* This wrapper component maintains backward compatibility for existing code.
|
||||
* For new code, prefer:
|
||||
* - EmptyStateCompact with assetKey for simple states
|
||||
* - EmptyStateDetailed with assetKey for detailed states
|
||||
*/
|
||||
|
||||
type TEmptyStateType = "detailed" | "simple";
|
||||
type EmptyStateType = "detailed" | "simple";
|
||||
|
||||
export interface EmptyStateProps {
|
||||
/** @deprecated Use assetKey instead */
|
||||
asset?: React.ReactNode;
|
||||
title: string;
|
||||
title?: string;
|
||||
description?: string;
|
||||
actions?: ActionButton[];
|
||||
actions?: BaseEmptyStateCommonProps["actions"];
|
||||
className?: string;
|
||||
type?: TEmptyStateType;
|
||||
rootClassName?: string;
|
||||
assetClassName?: string;
|
||||
type?: EmptyStateType;
|
||||
/** Type-safe asset key (use instead of asset) */
|
||||
assetKey?: CompactAssetType | DetailedAssetType;
|
||||
}
|
||||
|
||||
const EmptyStateContent: React.FC<{
|
||||
title: string;
|
||||
description?: string;
|
||||
actions?: ActionButton[];
|
||||
}> = ({ title, description, actions }) => (
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex flex-col gap-2">
|
||||
<h3 className="text-lg leading-7 font-semibold text-custom-text-100">{title}</h3>
|
||||
{description && <p className="text-sm leading-5 text-custom-text-300">{description}</p>}
|
||||
</div>
|
||||
|
||||
{actions && actions.length > 0 && (
|
||||
<div className="flex flex-col sm:flex-row gap-4">
|
||||
{actions.map((action, index) => (
|
||||
<Button key={index} onClick={action.onClick} disabled={action.disabled} variant={action.variant}>
|
||||
{action.label}
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
export const EmptyState: React.FC<EmptyStateProps> = ({
|
||||
type = "detailed",
|
||||
asset,
|
||||
assetKey,
|
||||
title,
|
||||
description,
|
||||
actions,
|
||||
className = "",
|
||||
type = "detailed",
|
||||
className,
|
||||
rootClassName,
|
||||
assetClassName,
|
||||
}) => {
|
||||
const alignmentClass = type === "simple" ? "items-center text-center" : "text-left";
|
||||
if (type === "simple") {
|
||||
return (
|
||||
<EmptyStateCompact
|
||||
asset={asset}
|
||||
assetKey={assetKey}
|
||||
title={title || description} // For simple type, use description as title if no title
|
||||
actions={actions}
|
||||
className={className}
|
||||
rootClassName={rootClassName}
|
||||
assetClassName={assetClassName}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={`flex flex-col gap-6 justify-center ${alignmentClass} max-w-[25rem] ${className}`}>
|
||||
{asset && <div className="flex items-center max-w-40">{asset}</div>}
|
||||
<EmptyStateContent title={title} description={description} actions={actions} />
|
||||
</div>
|
||||
<EmptyStateDetailed
|
||||
asset={asset}
|
||||
assetKey={assetKey}
|
||||
title={title}
|
||||
description={description}
|
||||
actions={actions}
|
||||
className={className}
|
||||
rootClassName={rootClassName}
|
||||
assetClassName={assetClassName}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
EmptyState.displayName = "EmptyState";
|
||||
|
|
|
|||
|
|
@ -1 +1,5 @@
|
|||
export * from "./assets";
|
||||
export * from "./compact-empty-state";
|
||||
export * from "./detailed-empty-state";
|
||||
export * from "./empty-state";
|
||||
export * from "./types";
|
||||
|
|
|
|||
24
packages/propel/src/empty-state/types.ts
Normal file
24
packages/propel/src/empty-state/types.ts
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
import type { TButtonVariant } from "../button/helper";
|
||||
import type { TAlign } from "../utils/placement";
|
||||
import type { CompactAssetType, DetailedAssetType } from "./assets/asset-types";
|
||||
|
||||
export interface ActionButton extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "children"> {
|
||||
label: string;
|
||||
variant?: TButtonVariant;
|
||||
[key: `data-${string}`]: string | undefined;
|
||||
}
|
||||
|
||||
export interface BaseEmptyStateCommonProps {
|
||||
title?: string;
|
||||
actions?: ActionButton[];
|
||||
/** CSS classes for the content wrapper */
|
||||
className?: string;
|
||||
/** CSS classes for the root container */
|
||||
rootClassName?: string;
|
||||
/** CSS classes for the asset wrapper */
|
||||
assetClassName?: string;
|
||||
description?: string;
|
||||
assetKey?: CompactAssetType | DetailedAssetType;
|
||||
asset?: React.ReactNode;
|
||||
align?: TAlign;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue