-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #58 from Vizzuality/SKY30-133-contact-us-page
[SKY30-133]: Contact page: contact form
- Loading branch information
Showing
14 changed files
with
2,199 additions
and
1,481 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
NEXT_PUBLIC_API_URL=http://0.0.0.0:1337/api | ||
NEXT_PUBLIC_MAPBOX_API_TOKEN= | ||
HUBSPOT_TOKEN= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
import * as React from 'react'; | ||
|
||
import { | ||
Controller, | ||
ControllerProps, | ||
FieldPath, | ||
FieldValues, | ||
FormProvider, | ||
useFormContext, | ||
} from 'react-hook-form'; | ||
|
||
import * as LabelPrimitive from '@radix-ui/react-label'; | ||
import { Slot } from '@radix-ui/react-slot'; | ||
|
||
import { Label } from '@/components/ui/label'; | ||
import { cn } from '@/lib/classnames'; | ||
|
||
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 { formItemId } = useFormField(); | ||
|
||
return ( | ||
<Label | ||
ref={ref} | ||
className={cn('font-mono text-xs font-normal', 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-slate-500 dark:text-slate-400', 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-red-500 dark:text-red-900', className)} | ||
{...props} | ||
> | ||
{body} | ||
</p> | ||
); | ||
}); | ||
FormMessage.displayName = 'FormMessage'; | ||
|
||
export { | ||
useFormField, | ||
Form, | ||
FormItem, | ||
FormLabel, | ||
FormControl, | ||
FormDescription, | ||
FormMessage, | ||
FormField, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
import * as React from 'react'; | ||
|
||
import * as SelectPrimitive from '@radix-ui/react-select'; | ||
import { Check, ChevronDown, ChevronUp } from 'lucide-react'; | ||
|
||
import { cn } from '@/lib/classnames'; | ||
|
||
const Select = SelectPrimitive.Root; | ||
|
||
const SelectGroup = SelectPrimitive.Group; | ||
|
||
const SelectValue = SelectPrimitive.Value; | ||
|
||
const SelectTrigger = React.forwardRef< | ||
React.ElementRef<typeof SelectPrimitive.Trigger>, | ||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger> | ||
>(({ className, children, ...props }, ref) => ( | ||
<SelectPrimitive.Trigger | ||
ref={ref} | ||
className={cn( | ||
'dark:bg-slate-950 dark:ring-offset-slate-950 flex h-10 w-full items-center justify-between border border-black bg-white px-3 py-2 text-sm ring-offset-white placeholder:text-slate-500 focus:outline-none focus:ring-2 focus:ring-black focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 dark:border-slate-800 dark:placeholder:text-slate-400 dark:focus:ring-slate-300 [&>span]:line-clamp-1', | ||
className | ||
)} | ||
{...props} | ||
> | ||
{children} | ||
<SelectPrimitive.Icon asChild> | ||
<ChevronDown className="h-4 w-4 opacity-50" /> | ||
</SelectPrimitive.Icon> | ||
</SelectPrimitive.Trigger> | ||
)); | ||
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName; | ||
|
||
const SelectScrollUpButton = React.forwardRef< | ||
React.ElementRef<typeof SelectPrimitive.ScrollUpButton>, | ||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton> | ||
>(({ className, ...props }, ref) => ( | ||
<SelectPrimitive.ScrollUpButton | ||
ref={ref} | ||
className={cn('flex cursor-default items-center justify-center py-1', className)} | ||
{...props} | ||
> | ||
<ChevronUp className="h-4 w-4" /> | ||
</SelectPrimitive.ScrollUpButton> | ||
)); | ||
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName; | ||
|
||
const SelectScrollDownButton = React.forwardRef< | ||
React.ElementRef<typeof SelectPrimitive.ScrollDownButton>, | ||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton> | ||
>(({ className, ...props }, ref) => ( | ||
<SelectPrimitive.ScrollDownButton | ||
ref={ref} | ||
className={cn('flex cursor-default items-center justify-center py-1', className)} | ||
{...props} | ||
> | ||
<ChevronDown className="h-4 w-4" /> | ||
</SelectPrimitive.ScrollDownButton> | ||
)); | ||
SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName; | ||
|
||
const SelectContent = React.forwardRef< | ||
React.ElementRef<typeof SelectPrimitive.Content>, | ||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content> | ||
>(({ className, children, position = 'popper', ...props }, ref) => ( | ||
<SelectPrimitive.Portal> | ||
<SelectPrimitive.Content | ||
ref={ref} | ||
className={cn( | ||
'text-slate-950 dark:bg-slate-950 relative z-50 max-h-96 min-w-[8rem] overflow-hidden border border-black bg-white shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 dark:border-slate-800 dark:text-slate-50', | ||
position === 'popper' && | ||
'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1', | ||
className | ||
)} | ||
position={position} | ||
{...props} | ||
> | ||
<SelectScrollUpButton /> | ||
<SelectPrimitive.Viewport | ||
className={cn( | ||
'p-1', | ||
position === 'popper' && | ||
'h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]' | ||
)} | ||
> | ||
{children} | ||
</SelectPrimitive.Viewport> | ||
<SelectScrollDownButton /> | ||
</SelectPrimitive.Content> | ||
</SelectPrimitive.Portal> | ||
)); | ||
SelectContent.displayName = SelectPrimitive.Content.displayName; | ||
|
||
const SelectLabel = React.forwardRef< | ||
React.ElementRef<typeof SelectPrimitive.Label>, | ||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label> | ||
>(({ className, ...props }, ref) => ( | ||
<SelectPrimitive.Label | ||
ref={ref} | ||
className={cn('py-1.5 pl-8 pr-2 text-sm font-semibold', className)} | ||
{...props} | ||
/> | ||
)); | ||
SelectLabel.displayName = SelectPrimitive.Label.displayName; | ||
|
||
const SelectItem = React.forwardRef< | ||
React.ElementRef<typeof SelectPrimitive.Item>, | ||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item> | ||
>(({ className, children, ...props }, ref) => ( | ||
<SelectPrimitive.Item | ||
ref={ref} | ||
className={cn( | ||
'relative flex w-full cursor-default select-none items-center py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-slate-100 focus:text-slate-900 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 dark:focus:bg-slate-800 dark:focus:text-slate-50', | ||
className | ||
)} | ||
{...props} | ||
> | ||
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center"> | ||
<SelectPrimitive.ItemIndicator> | ||
<Check className="h-4 w-4" /> | ||
</SelectPrimitive.ItemIndicator> | ||
</span> | ||
|
||
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText> | ||
</SelectPrimitive.Item> | ||
)); | ||
SelectItem.displayName = SelectPrimitive.Item.displayName; | ||
|
||
const SelectSeparator = React.forwardRef< | ||
React.ElementRef<typeof SelectPrimitive.Separator>, | ||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator> | ||
>(({ className, ...props }, ref) => ( | ||
<SelectPrimitive.Separator | ||
ref={ref} | ||
className={cn('-mx-1 my-1 h-px bg-slate-100 dark:bg-slate-800', className)} | ||
{...props} | ||
/> | ||
)); | ||
SelectSeparator.displayName = SelectPrimitive.Separator.displayName; | ||
|
||
export { | ||
Select, | ||
SelectGroup, | ||
SelectValue, | ||
SelectTrigger, | ||
SelectContent, | ||
SelectLabel, | ||
SelectItem, | ||
SelectSeparator, | ||
SelectScrollUpButton, | ||
SelectScrollDownButton, | ||
}; |
Oops, something went wrong.