Skip to content

Commit

Permalink
Merge pull request #390 from omnifed/388-feature-make-fileuploader-co…
Browse files Browse the repository at this point in the history
…mponent

388 feature make fileuploader component
  • Loading branch information
caseybaggz authored Aug 20, 2024
2 parents 572b794 + 9cc759e commit 7fa6bfb
Show file tree
Hide file tree
Showing 28 changed files with 863 additions and 42 deletions.
9 changes: 7 additions & 2 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,16 @@ updates:
interval: weekly

- package-ecosystem: npm
directory: '/packages/panda-preset'
directory: '/packages/*'
schedule:
interval: weekly

- package-ecosystem: npm
directory: '/packages/react'
directory: '/docs'
schedule:
interval: weekly

- package-ecosystem: npm
directory: '/figma'
schedule:
interval: weekly
2 changes: 1 addition & 1 deletion docs/app/data/categories.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,6 @@
"inputs": {
"name": "Inputs",
"description": "Components that allow users to input data.",
"items": ["Input", "Textarea"]
"items": ["Input", "Textarea", "File Uploader"]
}
}
2 changes: 2 additions & 0 deletions docs/app/preset/conditions/doc.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ Cerberus extends the [built-in conditions](https://panda-css.com/docs/concepts/c
| _darkMode | `[data-color-mode=dark] &, &.dark, .dark &` |
| _modalOpen | `&:is([data-modal-open=true])` |
| _screenReaderOnly | `&:is([data-screen-reader-only=true])` |
| _isOver | `&:is([data-over=true])` |
| _isDropped | `&:is([data-dropped=true])` |
| _invalid | `&:is(:invalid, [data-invalid], [aria-invalid])` |
| _userInvalid | `&:is(:user-invalid, [aria-invalid])` |
| _groupInvalid | `.group:is([data-invalid] &, [aria-invalid]) &` |
Expand Down
61 changes: 61 additions & 0 deletions docs/app/react/drag-n-drop/components/dnd-preview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
'use client'

import {
Button,
DndContext,
Droppable,
Show,
type DragEndEvent,
} from '@cerberus-design/react'
import { useCallback, useState } from 'react'
import { Draggable } from './draggable'
import { css } from '@cerberus/styled-system/css'

const draggableMarkup = <Draggable>Drag me</Draggable>

export default function DnDPreview() {
const [isDropped, setIsDropped] = useState<boolean>(false)

const handleDragEnd = useCallback((event: DragEndEvent) => {
if (event.over && event.over.id === 'droppable') {
setIsDropped(true)
}
}, [])

const handleReset = useCallback(() => {
setIsDropped(false)
}, [])

return (
<DndContext onDragEnd={handleDragEnd}>
<Show
when={!isDropped}
fallback={<Button onClick={handleReset}>Reset</Button>}
>
{draggableMarkup}
</Show>

<Droppable
className={css({
bgColor: 'page.surface.200',
border: '4px dashed',
borderColor: 'page.border.100',
h: '8rem',
p: '4',
rounded: 'lg',
transitionProperty: 'border-color',
transitionDuration: '150ms',
w: '1/2',
_isOver: {
borderColor: 'success.border.initial',
},
})}
id="droppable"
>
<Show when={isDropped} fallback={<>Drop here</>}>
{draggableMarkup}
</Show>
</Droppable>
</DndContext>
)
}
34 changes: 34 additions & 0 deletions docs/app/react/drag-n-drop/components/draggable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { useDraggable } from '@cerberus-design/react'
import { css } from '@cerberus/styled-system/css'
import type { ButtonHTMLAttributes, PropsWithChildren } from 'react'

export function Draggable(
props: PropsWithChildren<ButtonHTMLAttributes<HTMLButtonElement>>,
) {
const { attributes, listeners, setNodeRef, transform } = useDraggable({
id: 'draggable',
})
const style = transform
? {
transform: `translate3d(${transform.x}px, ${transform.y}px, 0)`,
}
: undefined

return (
<button
className={css({
bgColor: 'info.bg.initial',
cursor: 'grab',
p: '4',
rounded: 'lg',
userSelect: 'none',
})}
ref={setNodeRef}
style={style}
{...listeners}
{...attributes}
>
{props.children}
</button>
)
}
98 changes: 98 additions & 0 deletions docs/app/react/drag-n-drop/doc.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
---
heading: 'Drag and Drop'
description: 'Drag and drop allow users to drag and drop items in the UI.'
a11y: 'utilities'
npm: '@cerberus-design/react'
source: 'components/Draggable.tsx'
recipe: ''
---

import {
WhenToUseAdmonition,
WhenNotToUseAdmonition,
} from '@/app/components/Admonition'
import CodePreview from '@/app/components/CodePreview'
import DnDPreview from '@/app/react/drag-n-drop/components/dnd-preview'

```ts
import {
DndContext,
Droppable,
useDraggable,
useDroppable,
} from '@cerberus-design/react'
```

<WhenToUseAdmonition description="When you need add drag and drop functionality to the UI." />

## Usage

<CodePreview preview={<DnDPreview />}>
```tsx title="nav.tsx"
'use client'

import {
Button,
DndContext,
Droppable,
Show,
type DragEndEvent,
} from '@cerberus-design/react'
import { useCallback, useState } from 'react'
import { Draggable } from './draggable'
import { css } from '@cerberus/styled-system/css'

const draggableMarkup = <Draggable>Drag me</Draggable>

function DnDPreview() {
const [isDropped, setIsDropped] = useState<boolean>(false)

const handleDragEnd = useCallback((event: DragEndEvent) => {
if (event.over && event.over.id === 'droppable') {
setIsDropped(true)
}
}, [])

const handleReset = useCallback(() => {
setIsDropped(false)
}, [])

return (
<DndContext onDragEnd={handleDragEnd}>
<Show
when={!isDropped}
fallback={<Button onClick={handleReset}>Reset</Button>}
>
{draggableMarkup}
</Show>

<Droppable
className={css({
bgColor: 'page.surface.200',
border: '4px dashed',
borderColor: 'page.border.100',
h: '8rem',
p: '4',
rounded: 'lg',
transitionProperty: 'border-color',
transitionDuration: '150ms',
w: '1/2',
_isOver: {
borderColor: 'success.border.initial',
},
})}
id="droppable"
>
<Show when={isDropped} fallback={<>Drop here</>}>
{draggableMarkup}
</Show>
</Droppable>
</DndContext>
)
}
```
</CodePreview>

## API

This API is a direct export of the [dnd kit library](https://dndkit.com/)
28 changes: 28 additions & 0 deletions docs/app/react/drag-n-drop/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import ApiLinks from '@/app/components/ApiLinks'
import OnThisPage from '../../components/OnThisPage'
import { PageMainContent, PageSections } from '../../components/PageLayout'
import Doc, { frontmatter } from './doc.mdx'
import FeatureHeader from '@/app/components/FeatureHeader'
import type { MatchFeatureKind } from '@/app/components/MatchFeatureImg'

export default function DnDPage() {
return (
<>
<PageMainContent>
<FeatureHeader
heading={frontmatter.heading}
description={frontmatter.description}
a11y={frontmatter.a11y as MatchFeatureKind}
/>
<ApiLinks {...frontmatter} />
<main>
<Doc />
</main>
</PageMainContent>

<PageSections>
<OnThisPage />
</PageSections>
</>
)
}
33 changes: 33 additions & 0 deletions docs/app/react/file-uploader/a11y.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
---

import {
WhenToUseAdmonition
} from '@/app/components/Admonition'
import OverviewList from '@/app/components/OverviewList'

## Use Cases

<OverviewList intro="Users should be able to:" rules={[
'Navigate to and activate a file uploader with assistive technology',
'Select files to upload',
'Receive feedback on the status of the upload',
]} />

## Interaction &amp; Style

Changes to color and thickness of stroke help provide clear visual cues for interaction.

## Keyboard Navigation

| Keys | Actions |
| -------- | --------------------------------------------------------------- |
| Tab | Focus lands on (non-disabled) input |
| Enter | Opens the file dialog to select a file to upload |
| Space | Opens the file dialog to select a file to upload |

## Labeling Elements

If the UI text is correctly linked, assistive tech (such as a screenreader) will read the UI text followed by the component's role.

The accessibility label for a input is typically the same as the label for the input.
11 changes: 11 additions & 0 deletions docs/app/react/file-uploader/components/file-uploader-preview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { FileUploader } from '@cerberus-design/react'

export function BasicFileUploader() {
return (
<FileUploader
accept=".csv,.docx"
heading="Upload Files"
name="basic-example"
/>
)
}
57 changes: 57 additions & 0 deletions docs/app/react/file-uploader/dev.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
---
npm: '@cerberus-design/react'
source: 'components/Label.tsx'
recipe: 'label.ts'
---

import CodePreview from '@/app/components/CodePreview'
import {
BasicFileUploader
} from '@/app/react/file-uploader/components/file-uploader-preview'

```ts
import { FileUploader } from '@cerberus-design/react'
```

## Usage

<CodePreview preview={<BasicFileUploader />}>
```tsx title="file-uploader.tsx" {5,6}
import { FileUploader } from '@cerberus-design/react'

function BasicFileUploader() {
return (
<FileUploader
accept=".csv,.docx"
heading="Upload Files"
name="basic-example"
/>
)
}
```
</CodePreview>

## Customizing

To customize the FileUploader, we recommend extending the `fileUploader` slot recipe in your panda config.

## API

```ts showLineNumbers=false
export interface FileUploaderProps
extends InputHTMLAttributes<HTMLInputElement> {
heading?: string
name: string
}

define function FileUploader(props: FileUploaderProps): ReactNode
```

### Props

The `FileUploader` component accepts the following props:

| Name | Default | Description |
| -------- | ------- | ------------------------------------------------------------- |
| heading | | The heading for the file uploader |
| name | | The unique name of the file uploader |
32 changes: 32 additions & 0 deletions docs/app/react/file-uploader/guidelines.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
---

import CodePreview from '@/app/components/CodePreview'
import OverviewList from '@/app/components/OverviewList'
import {
WhenToUseAdmonition
} from '@/app/components/Admonition'
import {
BasicFileUploader
} from '@/app/react/file-uploader/components/file-uploader-preview'

## Usage

<WhenToUseAdmonition description="When you need to provide a way for users to upload files to the server." />

## File Uploader

File Uploaders can be used for any type of file (i.e. text, images, video, etc.).

<CodePreview preview={<BasicFileUploader />} />

## Placement

File Uploaders should be placed in a location that is easy to find and use.

<OverviewList intro="They can be placed in:" rules={[
'Forms',
'Modals',
'Pages',
'Sidebars',
]} />
Loading

0 comments on commit 7fa6bfb

Please sign in to comment.