Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/dev' into feat/landing-page-intr…
Browse files Browse the repository at this point in the history
…o-card-events-and-projects-section
  • Loading branch information
OMGATE23 committed Jul 26, 2023
2 parents 017785b + c5d8c8b commit 7143d56
Show file tree
Hide file tree
Showing 10 changed files with 360 additions and 1 deletion.
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ module.exports = {
'prefer-const': 'error',
'prefer-arrow-callback': 'warn',
'react/no-array-index-key': 'warn',
'react/button-has-type': 'warn',
},
settings: {
'import/resolver': {
Expand Down
3 changes: 3 additions & 0 deletions public/assets/contact-us-bg.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
43 changes: 43 additions & 0 deletions src/components/Button/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,48 @@
/* eslint-disable react/button-has-type */
import { DiscordLogo } from '@phosphor-icons/react';
import { ButtonTypes, Button as IButton } from '../../types';
import { ButtonHTMLAttributes } from 'react';

enum ButtonVariants {
PRIMARY = 'primary',
}
interface IBaseButton extends ButtonHTMLAttributes<HTMLButtonElement> {
LeftIcon?: () => JSX.Element;
RightIcon?: () => JSX.Element;
title?: string;
variant?: ButtonVariants;
extendedClassNames?: string;
}

const buttonStyles = {
primary: 'rounded-full border border-solid border-current font-medium',
};

export function BaseButton({
title,
type,
LeftIcon,
RightIcon,
extendedClassNames,
variant = ButtonVariants.PRIMARY,
}: IBaseButton) {
function getVariantStyles() {
return buttonStyles[variant];
}

return (
<button
type={type || 'button'}
className={`px-[clamp(20px,2.9vw,40px)] py-[clamp(12px,1.25vw,24px)] flex items-center justify-center gap-4 ${getVariantStyles()} ${
extendedClassNames ? extendedClassNames : null
}`}
>
{LeftIcon ? <LeftIcon /> : null}
{title}
{RightIcon ? <RightIcon /> : null}
</button>
);
}

export function Button({ link, type }: IButton) {
return (
Expand Down
71 changes: 71 additions & 0 deletions src/components/ContactForm/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { useMediaQuery } from '../../hooks';
import { ScreenTypes } from '../../types';
import { BaseButton } from '../Button';
import { Dropdown } from '../Dropdown';
import { Input, TextArea } from '../Inputs';

function RightArrowIcon() {
return (
<span className='block'>
<svg
className='mt-1 -mr-3 max-w-full'
width='40'
height='15'
viewBox='0 0 40 15'
fill='none'
xmlns='http://www.w3.org/2000/svg'
>
<path
d='M0.999939 7.21548L37.5654 7.13593M37.5654 7.13593L31.2899 13.6611M37.5654 7.13593L30.9954 1.5092'
stroke='black'
strokeWidth='2'
/>
</svg>
</span>
);
}

export function ContactForm() {
const { screenType } = useMediaQuery();
return (
<form>
<div className='p-[clamp(19px,2.1vw,36px)] rounded bg-primary shadow-contact-form flex flex-col gap-[clamp(16px,1.87vw,32px)]'>
<Input name='name' labelTitle='Your Name' inputAttributes={{ placeholder: 'John Doe' }} />
<Input
name='email'
labelTitle='Your Email'
inputAttributes={{ placeholder: '[email protected]' }}
/>
<Dropdown
name='interests'
labelTitle='Your Interests'
options={[
{ title: 'Web Development', value: 'webDevelopment', isDefaultValue: true },
{ title: 'App Development', value: 'appDevelopment' },
{ title: 'Devops', value: 'devops' },
{ title: 'Big Data Analysis', value: 'bigDataAnalysis' },
{ title: 'Web3', value: 'web3' },
{ title: 'Big Data ssAnalysis', value: 'bigDadtaAnalysis' },
{ title: 'Wseb3', value: 'wedwb3' },
]}
/>
<TextArea
name='message'
labelTitle='Message'
textAreaAttributes={{
placeholder: 'Let us know about you project',
rows: 5,
maxLength: 300,
}}
/>
</div>
<BaseButton
title='Send Message'
extendedClassNames={`mt-[clamp(24px,3.3vw,44px)] mx-auto ${
screenType === ScreenTypes.MOBILE ? 'text-[13px]' : null
}`}
RightIcon={RightArrowIcon}
/>
</form>
);
}
81 changes: 81 additions & 0 deletions src/components/Dropdown/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { useState } from 'react';
import { Input, ScreenTypes } from '../../types';
import { Label } from '../Inputs';
import { CaretDown, CaretUp } from '@phosphor-icons/react';
import { useMediaQuery } from '../../hooks';

interface Option {
title: string;
value: string;
isDefaultValue?: boolean;
}

interface IDropdown extends Omit<Input, 'inputAttributes'> {
options: Option[];
}

export function Dropdown({ options, labelTitle, disabled, error, labelAttributes }: IDropdown) {
const { screenType } = useMediaQuery();
const [isOptionsMenuOpen, setIsOptionsMenuOpen] = useState(false);
const [selectedOption, setSelectedOption] = useState(() =>
options.find(({ isDefaultValue }) => isDefaultValue),
);
const onSmallScreen = screenType === ScreenTypes.MOBILE || screenType === ScreenTypes.TABLET;

function toggleMenu() {
setIsOptionsMenuOpen((prev) => !prev);
}

return (
<div className='flex flex-col'>
{labelTitle ? (
<Label
{...labelAttributes}
labelTitle={labelTitle}
disabled={disabled}
error={error}
onClick={toggleMenu}
/>
) : null}
<div className='flex w-full justify-center relative'>
<button
type='button'
onClick={toggleMenu}
className={`input-basic w-full relative text-start grid grid-cols-[1fr,15px] border border-solid transition-colors ${
isOptionsMenuOpen ? 'border-current' : 'border-transparent'
}`}
>
{selectedOption?.title ? <span>{selectedOption.title}</span> : null}
<span className='self-end flex h-full items-center'>
{isOptionsMenuOpen ? <CaretUp /> : <CaretDown />}
</span>
</button>
{isOptionsMenuOpen ? (
<ul
className={`absolute top-[calc(100%+4px)] w-full bg-input-bg rounded-md overflow-y-auto ${
onSmallScreen ? 'max-h-[150px]' : 'max-h-[180px]'
}`}
>
{options.map((option) => (
<li key={option.value}>
<button
type='button'
onClick={() => {
setSelectedOption(option);
setIsOptionsMenuOpen(false);
}}
className='input-basic w-full text-start transition-colors hover:bg-[#EAEAEA]'
>
{option.title}
</button>
</li>
))}
</ul>
) : null}
</div>
{error && !disabled ? (
<span className='text-error mt-1 text-xs font-semibold'>{error}</span>
) : null}
</div>
);
}
93 changes: 93 additions & 0 deletions src/components/Inputs/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { TextareaHTMLAttributes } from 'react';
import { Input as IInput, Label as ILabel } from '../../types';

export function Label({ disabled, error, labelTitle, ...props }: ILabel) {
return (
<label
{...props}
htmlFor={labelTitle}
className={`text-sm mb-2 ${(() => {
if (disabled) {
return 'text-disabled-gray';
} else if (error) {
return 'text-error';
}
return 'text-current';
})()}`}
>
{labelTitle}
</label>
);
}

export function Input({
labelTitle,
name,
error,
disabled,
labelAttributes,
inputAttributes,
}: IInput) {
return (
<div className='flex flex-col'>
{labelTitle ? (
<Label labelTitle={labelTitle} disabled={disabled} error={error} {...labelAttributes} />
) : null}
<input
{...inputAttributes}
name={name}
id={labelTitle}
disabled={disabled}
className={`input-basic border border-solid transition-colors ${(() => {
if (disabled) {
return 'border-disabled-input';
} else if (error) {
return 'border-error';
}
return 'border-transparent focus:border-current';
})()}`}
/>
{error && !disabled ? (
<span className='text-error mt-1 text-xs font-semibold'>{error}</span>
) : null}
</div>
);
}

interface ITextArea extends Omit<IInput, 'inputAttributes'> {
textAreaAttributes?: TextareaHTMLAttributes<HTMLTextAreaElement>;
}

export function TextArea({
labelTitle,
name,
error,
disabled,
labelAttributes,
textAreaAttributes,
}: ITextArea) {
return (
<div className='flex flex-col'>
{labelTitle ? (
<Label labelTitle={labelTitle} disabled={disabled} error={error} {...labelAttributes} />
) : null}
<textarea
{...textAreaAttributes}
name={name}
id={labelTitle}
disabled={disabled}
className={`input-basic border border-solid transition-colors resize-none ${(() => {
if (disabled) {
return 'border-disabled-input';
} else if (error) {
return 'border-error';
}
return 'border-transparent focus:border-current';
})()}`}
/>
{error && !disabled ? (
<span className='text-error mt-1 text-xs font-semibold'>{error}</span>
) : null}
</div>
);
}
3 changes: 3 additions & 0 deletions src/components/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export * from './ContactForm';
export * from './Dropdown';
export * from './Header';
export * from './Hero';
export * from './Button';
Expand All @@ -6,3 +8,4 @@ export * from './MarqueeTitle';
export * from './ExploreEvents';
export * from './ExploreProjects';
export * from './Testimonials';
export * from './Inputs';
49 changes: 48 additions & 1 deletion src/routes/ContactUs/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,50 @@
import { Link } from 'react-router-dom';
import { ContactForm } from '../../components';
import { useMediaQuery } from '../../hooks';
import { ScreenTypes } from '../../types';

export function ContactUs() {
return <h1>This is contact us</h1>;
const { screenType } = useMediaQuery();
const onSmallScreen = screenType === ScreenTypes.MOBILE || screenType === ScreenTypes.TABLET;
return (
<main
style={{ backgroundImage: 'url(assets/contact-us-bg.svg)' }}
className={`${
onSmallScreen
? 'px-[max(20px,6vw)] py-[max(30px,4.25vw)]'
: 'px-[clamp(30px,10.3vw,155px)] py-[clamp(40px,6vw,100px)]'
} bg-no-repeat bg-bottom`}
>
<div
className={`text-[clamp(26px,3.2vw,55px)] capitalize pb-[clamp(32px,3.3vw,60px)] ${
onSmallScreen ? 'text-center' : null
}`}
>
<p className='font-semibold pb-1'>Get in touch</p>
<p>We&apos;d love to hear from you!</p>
</div>
<div className={`grid ${onSmallScreen ? 'grid-cols-[1fr]' : 'grid-cols-[3fr,2fr]'}`}>
<ContactForm />
{onSmallScreen ? null : (
<div className='flex flex-col items-center pt-2 justify-self-end'>
<div>
<p className='mb-6 text-[clamp(20px,1.8vw,28px)] font-medium'>Contact us</p>
<div className='flex gap-[clamp(16px,2.5vw,36px)]'>
{[
{ imgSrc: '', to: '', socialName: '' },
{ imgSrc: '', to: '', socialName: '' },
{ imgSrc: '', to: '', socialName: '' },
{ imgSrc: '', to: '', socialName: '' },
].map(({ imgSrc, socialName, to }) => (
<Link key={socialName} to={to}>
<div className='aspect-square w-9 h-9 rounded-full bg-slate-500' />
</Link>
))}
</div>
</div>
</div>
)}
</div>
</main>
);
}
1 change: 1 addition & 0 deletions src/types/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './common.types';
export * from './button.types';
export * from './input.types';
16 changes: 16 additions & 0 deletions src/types/input.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { InputHTMLAttributes, LabelHTMLAttributes } from 'react';

export interface Input {
name: string;
disabled?: boolean;
error?: string;
labelTitle?: string;
labelAttributes?: LabelHTMLAttributes<HTMLLabelElement>;
inputAttributes?: InputHTMLAttributes<HTMLInputElement>;
}

export interface Label extends LabelHTMLAttributes<HTMLLabelElement> {
labelTitle: string;
disabled?: boolean;
error?: string;
}

0 comments on commit 7143d56

Please sign in to comment.