Skip to content

Commit

Permalink
feat: setup upload post modal fields
Browse files Browse the repository at this point in the history
  • Loading branch information
acatzk committed Jan 27, 2024
1 parent 0bcf337 commit 28dcd34
Show file tree
Hide file tree
Showing 11 changed files with 611 additions and 177 deletions.
432 changes: 267 additions & 165 deletions components/modals/upload-post-modal.tsx

Large diffs are not rendered by default.

21 changes: 12 additions & 9 deletions components/tag-input.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,25 @@
import React from 'react'
import dynamic from 'next/dynamic'
import { WithContext as ReactTags } from 'react-tag-input'

import { Tag, delimiters, dummyTags } from '~/helpers/tag-helpers'

const ReactTags = dynamic(async () => (await import('react-tag-input')).WithContext, {
ssr: false
})
import { Tag, delimiters } from '~/helpers/tag-helpers'

export type TagInputProps = {
state: {
tags: Tag[]
setTags: React.Dispatch<React.SetStateAction<Tag[]>>
}
data:
| Array<{
id: number
tag: string
}>
| undefined
}

export const TagInput = (props: TagInputProps): JSX.Element => {
const {
state: { tags, setTags }
state: { tags, setTags },
data
} = props

const handleDeleteTag = (i: number): void => {
Expand All @@ -40,9 +43,9 @@ export const TagInput = (props: TagInputProps): JSX.Element => {
return (
<ReactTags
tags={tags}
suggestions={dummyTags?.map((hashtag) => ({
suggestions={data?.map((hashtag) => ({
id: hashtag.id.toString(),
text: hashtag.text
text: hashtag.tag
}))}
delimiters={delimiters}
handleDelete={handleDeleteTag}
Expand Down
169 changes: 169 additions & 0 deletions components/ui/form.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
/* eslint-disable */

import * as React from 'react'
import * as LabelPrimitive from '@radix-ui/react-label'
import { Slot } from '@radix-ui/react-slot'
import {
Controller,
ControllerProps,
FieldPath,
FieldValues,
FormProvider,
useFormContext
} from 'react-hook-form'

import { cn } from '~/lib/utils'
import { Label } from '~/components/ui/label'

const Form = FormProvider

type FormFieldContextValue<
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
> = {
name: TName
}

const FormFieldContext = React.createContext<FormFieldContextValue>({} as FormFieldContextValue)

const FormField = <
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
>({
...props
}: ControllerProps<TFieldValues, TName>) => {
return (
<FormFieldContext.Provider value={{ name: props.name }}>
<Controller {...props} />
</FormFieldContext.Provider>
)
}

const useFormField = () => {
const fieldContext = React.useContext(FormFieldContext)
const itemContext = React.useContext(FormItemContext)
const { getFieldState, formState } = useFormContext()

const fieldState = getFieldState(fieldContext.name, formState)

if (!fieldContext) {
throw new Error('useFormField should be used within <FormField>')
}

const { id } = itemContext

return {
id,
name: fieldContext.name,
formItemId: `${id}-form-item`,
formDescriptionId: `${id}-form-item-description`,
formMessageId: `${id}-form-item-message`,
...fieldState
}
}

type FormItemContextValue = {
id: string
}

const FormItemContext = React.createContext<FormItemContextValue>({} as FormItemContextValue)

const FormItem = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
({ className, ...props }, ref) => {
const id = React.useId()

return (
<FormItemContext.Provider value={{ id }}>
<div ref={ref} className={cn('space-y-2', className)} {...props} />
</FormItemContext.Provider>
)
}
)
FormItem.displayName = 'FormItem'

const FormLabel = React.forwardRef<
React.ElementRef<typeof LabelPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>
>(({ className, ...props }, ref) => {
const { error, formItemId } = useFormField()

return (
<Label
ref={ref}
className={cn(error && 'text-destructive', className)}
htmlFor={formItemId}
{...props}
/>
)
})
FormLabel.displayName = 'FormLabel'

const FormControl = React.forwardRef<
React.ElementRef<typeof Slot>,
React.ComponentPropsWithoutRef<typeof Slot>
>(({ ...props }, ref) => {
const { error, formItemId, formDescriptionId, formMessageId } = useFormField()

return (
<Slot
ref={ref}
id={formItemId}
aria-describedby={!error ? `${formDescriptionId}` : `${formDescriptionId} ${formMessageId}`}
aria-invalid={!!error}
{...props}
/>
)
})
FormControl.displayName = 'FormControl'

const FormDescription = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, ...props }, ref) => {
const { formDescriptionId } = useFormField()

return (
<p
ref={ref}
id={formDescriptionId}
className={cn('text-sm text-muted-foreground', className)}
{...props}
/>
)
})
FormDescription.displayName = 'FormDescription'

const FormMessage = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, children, ...props }, ref) => {
const { error, formMessageId } = useFormField()
const body = error ? String(error?.message) : children

if (!body) {
return null
}

return (
<p
ref={ref}
id={formMessageId}
className={cn('text-sm font-medium text-destructive', className)}
{...props}
>
{body}
</p>
)
})
FormMessage.displayName = 'FormMessage'

export {
useFormField,
Form,
FormItem,
FormLabel,
FormControl,
FormDescription,
FormMessage,
FormField
}
21 changes: 21 additions & 0 deletions components/ui/label.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
'use client'

import * as React from 'react'
import * as LabelPrimitive from '@radix-ui/react-label'
import { cva, type VariantProps } from 'class-variance-authority'

import { cn } from '~/lib/utils'

const labelVariants = cva(
'text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70'
)

const Label = React.forwardRef<
React.ElementRef<typeof LabelPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> & VariantProps<typeof labelVariants>
>(({ className, ...props }, ref) => (
<LabelPrimitive.Root ref={ref} className={cn(labelVariants(), className)} {...props} />
))
Label.displayName = LabelPrimitive.Root.displayName

export { Label }
29 changes: 29 additions & 0 deletions components/ui/switch.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
'use client'

import * as React from 'react'
import * as SwitchPrimitives from '@radix-ui/react-switch'

import { cn } from '~/lib/utils'

const Switch = React.forwardRef<
React.ElementRef<typeof SwitchPrimitives.Root>,
React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root>
>(({ className, ...props }, ref) => (
<SwitchPrimitives.Root
className={cn(
'peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors data-[state=checked]:bg-primary data-[state=unchecked]:bg-input focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50',
className
)}
{...props}
ref={ref}
>
<SwitchPrimitives.Thumb
className={cn(
'pointer-events-none block h-5 w-5 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0'
)}
/>
</SwitchPrimitives.Root>
))
Switch.displayName = SwitchPrimitives.Root.displayName

export { Switch }
23 changes: 23 additions & 0 deletions components/ui/textarea.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import * as React from 'react'

import { cn } from '~/lib/utils'

export interface TextareaProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {}

const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
({ className, ...props }, ref) => {
return (
<textarea
className={cn(
'flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
className
)}
ref={ref}
{...props}
/>
)
}
)
Textarea.displayName = 'Textarea'

export { Textarea }
71 changes: 71 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 28dcd34

Please sign in to comment.