Skip to content

Commit

Permalink
Merge pull request #75 from ruru-m07/feat/component/dropzone
Browse files Browse the repository at this point in the history
feat(dropzone): add dropzone component and documentation
  • Loading branch information
ruru-m07 authored Sep 24, 2024
2 parents 09dc93b + aa5583e commit 90e43a1
Show file tree
Hide file tree
Showing 15 changed files with 412 additions and 52 deletions.
10 changes: 10 additions & 0 deletions apps/www/app/playground/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ import {
selectAnimationVariants,
} from "ruru-ui/components/select";
import Modal, { ModalProvider } from "ruru-ui/components/modal";
import { Dropzone } from "ruru-ui/components/dropzone";
import AdvanceDropzone from "@/components/preview/dropzone/advanceDropzone";

const Playground = () => {
const handleSubmit = async () => {
Expand Down Expand Up @@ -607,6 +609,14 @@ const Playground = () => {
</ModalProvider>
</Card>

<Card className="p-10">
<Dropzone onDrop={(acceptedFiles) => console.log(acceptedFiles)} />
</Card>

<Card className="p-10">
<AdvanceDropzone />
</Card>

<div className="my-10" />
</div>
);
Expand Down
51 changes: 51 additions & 0 deletions apps/www/components/preview/dropzone/advanceDropzone.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"use client";

import React from "react";
import { Dropzone, RD } from "ruru-ui/components/dropzone";

import { File } from "lucide-react";

const AdvanceDropzone = () => {
const [files, setFiles] = React.useState<RD.FileWithPath[]>([]);

function formatFileSize(bytes: number) {
if (bytes === 0) return "0 Bytes";
const k = 1024;
const sizes = ["Bytes", "KB", "MB", "GB", "TB"];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i];
}

const onDrop = (acceptedFiles: RD.FileWithPath[]) => {
setFiles((p) => [...p, ...acceptedFiles]);
};

return (
<div className="w-full">
<Dropzone onDrop={onDrop} />
{files.length > 0 && (
<section className="my-4">
<h4 className="scroll-m-20 text-xl font-semibold tracking-tight">
Files
</h4>
{files.map((file, index) => (
<div
key={index}
className="space-x-3 w-[100%] border-[1.5px] border-input flex items-center justify-between rounded-md py-3 px-3 my-3"
>
<div className="flex items-center">
<File size={20} className="text-muted-foreground" />
<span className="ml-2 text-sm">{file.path}</span>
</div>
<span className="text-muted-foreground text-xs">
{formatFileSize(file.size)}
</span>
</div>
))}
</section>
)}
</div>
);
};

export default AdvanceDropzone;
15 changes: 15 additions & 0 deletions apps/www/components/preview/dropzone/dropzone.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
"use client";

import React from "react";
import { Wrapper } from "../wrapper";
import { Dropzone } from "ruru-ui/components/dropzone";

const DropzonePreview = () => {
return (
<Wrapper className="p-10">
<Dropzone />
</Wrapper>
);
};

export default DropzonePreview;
2 changes: 2 additions & 0 deletions apps/www/components/preview/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { BadgePreview } from "../badgePreview";
import Tabspreview from "../tabs";
import { default as ModalPreview } from "./Modal/preview";
import FormPreview from "./form/preview";
import DropzonePreview from "./dropzone/dropzone";

export default {
button: (
Expand Down Expand Up @@ -184,4 +185,5 @@ export default {
<FormPreview />
</Wrapper>
),
dropzone: <DropzonePreview />,
} as Record<string, ReactNode>;
155 changes: 155 additions & 0 deletions apps/www/content/docs/components/dropzone.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
---
title: Dropzone
description: Dropzone allows the user to drag and drop files to upload.
preview: dropzone
---

import { Tab, Tabs } from "fumadocs-ui/components/tabs";
import { Tabs as Rutabs, Tab as Rutab } from "ruru-ui/components/tabs";
import { Dropzone } from "ruru-ui/components/dropzone";
import AdvanceDropzone from "@/components/preview/dropzone/advanceDropzone";

## Installation

<Rutabs items={["CLI", "Dependencies"]}>
<Rutab value={"CLI"}>
<Tabs items={["npm", "pnpm", "yarn", "bun"]}>
<Tab value={"npm"}>
```bash
npx ruru-ui-cli@latest add dropzone
```
</Tab>
<Tab value={"pnpm"}>
```bash
pnpm dlx ruru-ui-cli@latest add dropzone
```
</Tab>
<Tab value={"yarn"}>
```bash
npx ruru-ui-cli@latest add dropzone
```
</Tab>
<Tab value={"bun"}>
```bash
bunx --bun ruru-ui-cli@latest add dropzone
```
</Tab>
</Tabs>

</Rutab>
<Rutab value={"Dependencies"}>
```package-install
npm install ruru-ui@latest
```
</Rutab>
</Rutabs>

## Usage

The `Dropzone` component is used to allow the user to drag and drop files to upload.
This component is a **wrapper** around the [react-dropzone](https://github.com/react-dropzone/react-dropzone) library.

<Tabs items={["Preview", "Code"]}>
<Tab className={"flex justify-center"} value="Preview" >
<div className="p-4 w-full">
<Dropzone />
</div>
</Tab>
<Tab className={"-mt-8"} value="Code">
```tsx
// [!code word:Dropzone]
import { Dropzone } from "ruru-ui/components/dropzone";

export function Demo() {
return <Dropzone onDrop={(acceptedFiles) => console.log(acceptedFiles)} />;
}
```
</Tab>
</Tabs>

---

### Advanced Example

In this example, we learn how to handle files using the Dropzone component.

<Tabs items={["Preview", "Code"]}>
<Tab className={"flex justify-center"} value="Preview" >
<div className="p-4 w-full">
<AdvanceDropzone className="m-10" />
</div>
</Tab>
<Tab className={"-mt-8"} value="Code">
```tsx
"use client";

import React from "react";
import { Dropzone, RD } from "ruru-ui/components/dropzone";

import { File } from "lucide-react";

const AdvanceDropzone = () => {
const [files, setFiles] = React.useState<RD.FileWithPath[]>([]);

function formatFileSize(bytes: number) {
if (bytes === 0) return "0 Bytes";
const k = 1024;
const sizes = ["Bytes", "KB", "MB", "GB", "TB"];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i];
}

const onDrop = (acceptedFiles: RD.FileWithPath[]) => {
setFiles((p) => [...p, ...acceptedFiles]);
};

return (
<div className="w-full">
<Dropzone onDrop={onDrop} />
{files.length > 0 && (
<section className="my-4">
<h4 className="scroll-m-20 text-xl font-semibold tracking-tight">
Files
</h4>
{files.map((file, index) => (
<div
key={index}
className="space-x-3 w-[100%] border-[1.5px] border-input flex items-center justify-between rounded-md py-3 px-3 my-3"
>
<div className="flex items-center">
<File size={20} className="text-muted-foreground" />
<span className="ml-2 text-sm">{file.path}</span>
</div>
<span className="text-muted-foreground text-xs">
{formatFileSize(file.size)}
</span>
</div>
))}
</section>
)}
</div>
);
};

export default AdvanceDropzone;
```
</Tab>
</Tabs>

## Import

We are exporting the `RD` component from the `react-dropzone` module.
Here RD is the alias for the `react-dropzone` library.

```tsx title="dropzone.tsx"
// [!code word:RD]
import * as RD from "react-dropzone";
export { RD };
```

## Props

| Name | Type | Default | Description |
| --------- | ------------------------------------------------- | ------- | --------------------------------------------------------- |
| className | string | "" | Optional additional class names for styling the dropzone. |
| props | DropzoneProps & React.RefAttributes\<DropzoneRef> | {} | Props passed to the `react-dropzone` component. |
16 changes: 0 additions & 16 deletions apps/www/next-sitemap.config.js
Original file line number Diff line number Diff line change
@@ -1,30 +1,14 @@
const { execSync } = require("child_process");

/** @type {import('next-sitemap').IConfig} */
module.exports = {
siteUrl: process.env.SITE_URL || "https://ruru-ui.vercel.app",
generateRobotsTxt: true,
exclude: ["/api/*"],
transform: async (config, path) => {
const lastmod = await getLastModifiedDate(path);
return {
loc: path,
changefreq: config.changefreq,
priority: config.priority,
lastmod,
alternateRefs: config.alternateRefs ?? [],
};
},
};

async function getLastModifiedDate(path) {
try {
const timestamp = execSync(`git log -1 --format=%ct ${path}`)
.toString()
.trim();
return new Date(parseInt(timestamp, 10) * 1000).toISOString();
} catch (error) {
console.error(`Error getting last modified date for ${path}:`, error);
return new Date().toISOString(); // Fallback to current date
}
}
1 change: 1 addition & 0 deletions apps/www/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"react": "^18.3.1",
"react-color-palette": "^7.3.0",
"react-dom": "^18.3.1",
"react-dropzone": "^14.2.3",
"react-resizable-panels": "^2.1.2",
"rehype-katex": "^7.0.0",
"remark-math": "^6.0.0",
Expand Down
11 changes: 11 additions & 0 deletions apps/www/public/registry/components/dropzone.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "dropzone",
"dependencies": ["react-dropzone"],
"files": [
{
"name": "dropzone.tsx",
"content": "\"use client\";\n\nimport React from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport { FilePlusIcon } from \"@radix-ui/react-icons\";\nimport ReactDropzone, { DropzoneProps, DropzoneRef } from \"react-dropzone\";\n\n\nexport const Dropzone = ({\n className,\n ...props\n}: DropzoneProps &\n React.RefAttributes<DropzoneRef> & {\n className?: string;\n }): React.ReactNode => {\n return (\n <>\n {}\n <div\n className={cn(\n \"items-center justify-center w-full p-5 border rounded-md bg-primary-foreground/65\",\n className,\n )}\n >\n {}\n <ReactDropzone {...props}>\n {({ getRootProps, getInputProps, isDragActive }) => (\n <section className=\"flex items-center justify-center h-96\">\n {}\n <div\n className=\"flex items-center justify-center w-full h-full\"\n {...getRootProps()}\n >\n <input {...getInputProps()} />\n {isDragActive ? (\n \n <div className=\"flex items-center justify-center w-full h-full border-2 border-dashed rounded-md\">\n <div className=\"flex flex-col justify-center items-center\">\n <FilePlusIcon\n className=\"mb-3 text-muted-foreground\"\n height={100}\n width={50}\n />\n <span className=\"text-muted-foreground\">\n Drop to Upload file\n </span>\n </div>\n </div>\n ) : (\n \n <div className=\"w-full flex flex-col justify-center items-center\">\n <FilePlusIcon\n className=\"mb-3 text-muted-foreground\"\n height={100}\n width={50}\n />\n <span id=\"dragzone-text\" className=\"text-muted-foreground\">\n Drag files here to add them to your repository\n </span>\n <span className=\"text-muted-foreground\">\n or{\" \"}\n <span className=\"text-blue-500 cursor-pointer hover:underline ml-1\">\n choose your file\n </span>\n </span>\n </div>\n )}\n </div>\n </section>\n )}\n </ReactDropzone>\n </div>\n </>\n );\n};\n\nimport * as RD from \"react-dropzone\";\nexport { RD };\n"
}
],
"type": "components:ui"
}
6 changes: 6 additions & 0 deletions apps/www/public/registry/index.json
Original file line number Diff line number Diff line change
Expand Up @@ -78,5 +78,11 @@
"name": "label",
"files": ["label.tsx"],
"type": "components:ui"
},
{
"name": "dropzone",
"dependencies": ["react-dropzone"],
"files": ["dropzone.tsx"],
"type": "components:ui"
}
]
3 changes: 3 additions & 0 deletions apps/www/public/robots.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,8 @@
User-agent: *
Allow: /

# Host
Host: https://ruru-ui.vercel.app

# Sitemaps
Sitemap: https://ruru-ui.vercel.app/sitemap.xml
Loading

0 comments on commit 90e43a1

Please sign in to comment.