Skip to content

Commit

Permalink
feat(upload): add storybook
Browse files Browse the repository at this point in the history
  • Loading branch information
xiaosen7 committed Aug 6, 2024
1 parent 1486001 commit d3102d4
Show file tree
Hide file tree
Showing 12 changed files with 400 additions and 46 deletions.
10 changes: 10 additions & 0 deletions libs/upload/.storybook/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import type { StorybookConfig } from "@storybook/nextjs";

import sharedConfig from "@npcs/storybook-config";

const config: StorybookConfig = {
...sharedConfig,
stories: ["../src/**/*.stories.tsx"],
};

export default config;
15 changes: 15 additions & 0 deletions libs/upload/.storybook/preview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type { Preview } from "@storybook/react";
import "../src/styles.css";

const preview: Preview = {
parameters: {
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/i,
},
},
},
};

export default preview;
7 changes: 5 additions & 2 deletions libs/upload/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@
"compile:watch": "rollup -c --watch",
"test": "vitest --run",
"test:watch": "vitest watch",
"test:changed": "vitest run --changed"
"test:changed": "vitest run --changed",
"storybook": "storybook dev -p 6006"
},
"exports": {
"./styles.css": "./esm/styles.css",
".": "./esm/index.js"
},
"dependencies": {
"@npcs/log": "workspace:^",
"@npcs/storybook-config": "workspace:^",
"@npcs/ui": "workspace:^",
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-label": "^2.1.0",
Expand All @@ -28,6 +28,7 @@
"@radix-ui/react-slot": "^1.1.0",
"@radix-ui/react-toggle": "^1.1.0",
"@radix-ui/react-toggle-group": "^1.1.0",
"@storybook/nextjs": "8.2.4",
"ahooks": "^3.8.0",
"async": "^3.2.5",
"change-case": "^5.4.4",
Expand All @@ -52,12 +53,14 @@
"devDependencies": {
"@npcs/eslint-config": "workspace:*",
"@npcs/rollup-config": "workspace:*",
"@npcs/storybook-config": "workspace:^",
"@npcs/tailwind-config": "workspace:*",
"@npcs/typescript-config": "workspace:*",
"@npcs/vitest-config": "workspace:^",
"@rollup/plugin-commonjs": "^26.0.1",
"@rollup/plugin-node-resolve": "^15.2.3",
"@rollup/plugin-typescript": "^11.1.6",
"@storybook/react": "8.2.4",
"@types/async": "^3.2.24",
"@types/lodash-es": "^4.17.12",
"@types/multistream": "^4.1.3",
Expand Down
36 changes: 36 additions & 0 deletions libs/upload/src/client/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
export enum EUploadClientState {
Default,
CalculatingHash,
CheckingFileExists,
FastUploaded,
WaitForUpload,
Uploading,
UploadStopped,
Merging,
UploadSuccessfully,
Error,
}

export interface IUploadChunkData {
hash: string;
chunk: Blob | Buffer;
index: number;
}

export interface IUploadClientActions {
fileExists: (hash: string) => Promise<boolean>;
chunkExists: (hash: string, index: number) => Promise<boolean>;
merge: (hash: string) => Promise<void>;
uploadChunk: (formData: FormData) => Promise<void>;
getLastExistedChunkIndex: (hash: string) => Promise<number>;
}

export interface IUploadClientJSON {
name: string;
size: number;
hash: string;
concurrency: number;
chunkSize: number;
poolElapse: number;
state: EUploadClientState;
}
42 changes: 5 additions & 37 deletions libs/upload/src/client/ui/client.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
import {
EUploadClientState,
IUploadClientActions,
IUploadClientJSON,
} from "@client/types";
import { once } from "lodash-es";
import memoize from "p-memoize";
import {
Expand All @@ -20,43 +25,6 @@ import { ERRORS } from "../errors";
import { createFormData } from "../form";
import { calculateChunksHashByWorker } from "../workers/calculate-hash";

export enum EUploadClientState {
Default,
CalculatingHash,
CheckingFileExists,
FastUploaded,
WaitForUpload,
Uploading,
UploadStopped,
Merging,
UploadSuccessfully,
Error,
}

export interface IUploadChunkData {
hash: string;
chunk: Blob | Buffer;
index: number;
}

export interface IUploadClientActions {
fileExists: (hash: string) => Promise<boolean>;
chunkExists: (hash: string, index: number) => Promise<boolean>;
merge: (hash: string) => Promise<void>;
uploadChunk: (formData: FormData) => Promise<void>;
getLastExistedChunkIndex: (hash: string) => Promise<number>;
}

export interface IUploadClientJSON {
name: string;
size: number;
hash: string;
concurrency: number;
chunkSize: number;
poolElapse: number;
state: EUploadClientState;
}

export class UploadClient {
static EState = EUploadClientState;

Expand Down
22 changes: 22 additions & 0 deletions libs/upload/src/client/ui/file.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Meta, StoryFn } from "@storybook/react";

import { ESupportedProtocol } from "@client/protocol";
import { EUploadClientState } from "@client/types";
import { File } from "./file";

export default {
component: File,
args: {},
} as Meta<typeof File>;

export const Base: StoryFn<typeof File> = () => (
<File
chunkSize={100}
concurrency={3}
elapse={999}
name="file.txt"
progress={61}
protocol={ESupportedProtocol.Http}
state={EUploadClientState.Uploading}
/>
);
165 changes: 165 additions & 0 deletions libs/upload/src/client/ui/file.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
import { EUploadClientState } from "@client/types";
import { Badge, cn, IComponentBaseProps, mp, Progress } from "@npcs/ui";
import {
CheckIcon,
Cross2Icon,
PauseIcon,
PlayIcon,
ReloadIcon,
TrashIcon,
} from "@radix-ui/react-icons";
import { memo } from "react";
import { formatFileSize } from "./format-file-size";
import { formatTimeBySeconds } from "./format-time";
import { Loading } from "./loading";
import { IObservableOrValue, Observer } from "./observer";

interface IUploadSingleFileProps extends IComponentBaseProps {
name: string;
chunkSize: number;
state: IObservableOrValue<EUploadClientState>;
progress: IObservableOrValue<number>;
elapse: IObservableOrValue<number>;
className?: string;
onRemove?: () => void;
concurrency: number;
protocol: string;
onPlay?: () => void;
onRestart?: () => void;
onStop?: () => void;
}

export const File = memo(function UploadSingleFile(
props: IUploadSingleFileProps,
) {
const {
name,
protocol,
state,
onRemove,
progress,
onPlay,
onRestart,
onStop,
concurrency,
chunkSize,
elapse,
} = props;
return mp(
props,
<div className="flex flex-col gap-2 px-4 py-2 hover:bg-gray-100">
<div className="flex items-center justify-between">
<div className="flex-1 truncate" title={name}>
{name}
</div>

<Badge className="mr-2 w-[90px] justify-center">{protocol}</Badge>
</div>

<Observer observable={state}>
{(state) => {
return (
<div
className="flex gap-2 overflow-hidden text-xs text-gray-500"
title={EUploadClientState[state]}
>
<UploadStateIcon state={state} />
<div className="flex-1 truncate">{EUploadClientState[state]}</div>
</div>
);
}}
</Observer>

<div className="flex items-center gap-2">
<Observer observable={progress}>
{(progress) => <Progress className="my-2 h-1" value={progress} />}
</Observer>
<div className="flex w-12 items-center justify-end">
<Observer observable={state}>
{(state) => (
<UploadControl
state={state}
onPlay={onPlay}
onRestart={onRestart}
onStop={onStop}
/>
)}
</Observer>
<TrashIcon className="ml-2 cursor-pointer" onClick={onRemove} />
</div>
</div>

<div className="flex gap-2 text-xs text-gray-400">
<div> Concurrency {concurrency} </div>
<div>, Chunk size {formatFileSize(chunkSize)} </div>
<Observer observable={elapse}>
{(elapsed) => <UploadingElapsed elapsed={elapsed} />}
</Observer>
</div>
</div>,
);
});

const UploadingElapsed: React.FC<{
elapsed: number;
}> = ({ elapsed }) => {
return (
<div className={cn(elapsed === 0 && "hidden")}>
, Uploading time {formatTimeBySeconds(elapsed / 10)}
</div>
);
};

const UploadStateIcon: React.FC<{
state: EUploadClientState;
}> = ({ state }) => {
switch (state) {
case EUploadClientState.Uploading:
case EUploadClientState.CheckingFileExists:
case EUploadClientState.CalculatingHash:
case EUploadClientState.Merging:
return <Loading />;

case EUploadClientState.UploadSuccessfully:
case EUploadClientState.FastUploaded:
return <CheckIcon color="green" />;

case EUploadClientState.Error:
return <Cross2Icon color="red" />;

default:
return null;
}
};

interface IUploadControlProps {
onPlay?: () => void;
onStop?: () => void;
onRestart?: () => void;
state: EUploadClientState;
}
const UploadControl: React.FC<IUploadControlProps> = ({
onPlay,
onStop,
onRestart,
state,
}) => {
switch (state) {
case EUploadClientState.WaitForUpload:
case EUploadClientState.UploadStopped:
return <PlayIcon className="cursor-pointer" onClick={onPlay} />;

case EUploadClientState.Uploading:
return <PauseIcon className="cursor-pointer" onClick={onStop} />;

case EUploadClientState.Error:
return <ReloadIcon className="cursor-pointer" onClick={onRestart} />;

default:
return null;
}
};

const ProgressUI: React.FC<{ progress: number }> = ({ progress }) => {
return <Progress className="my-2 h-1" value={progress} />;
};
13 changes: 7 additions & 6 deletions libs/upload/src/client/ui/format-file-size.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { filesize } from "filesize";
export function formatFileSize(value: number) {
return filesize(value, {
standard: "jedec",
});
}
import { filesize } from "filesize";

export function formatFileSize(value: number) {
return filesize(value, {
standard: "jedec",
});
}
17 changes: 17 additions & 0 deletions libs/upload/src/client/ui/observer.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Meta, StoryFn } from "@storybook/react";

import { interval } from "rxjs";
import { Observer } from "./observer";

export default {
component: Observer,
} as Meta<typeof Observer>;

export const Base: StoryFn<typeof Observer> = () => {
const observable = interval(1000);
return <Observer observable={observable}>{(x) => x}</Observer>;
};

export const PassValue: StoryFn<typeof Observer> = () => {
return <Observer observable={100}>{(x) => x}</Observer>;
};
Loading

0 comments on commit d3102d4

Please sign in to comment.