@@ -0,0 +1,2 @@ | |||
@tesseract-design:registry=http://localhost:4873 | |||
@modal-sh:registry=http://localhost:4873 |
@@ -4,13 +4,18 @@ import * as WebNavigationReact from '@tesseract-design/web-navigation-react'; | |||
import * as WebFreeformReact from '@tesseract-design/web-freeform-react'; | |||
import * as WebFormattedReact from '@tesseract-design/web-formatted-react'; | |||
import * as WebActionReact from '@tesseract-design/web-action-react'; | |||
import {Brand} from '@/components/molecules/Brand'; | |||
export interface ContactCtaBannerProps { | |||
onSubmit?: React.FormEventHandler<HTMLFormElement>; | |||
visible?: boolean; | |||
hasBrand?: boolean; | |||
} | |||
export const ContactCtaBanner: React.FC<ContactCtaBannerProps> = ({ | |||
onSubmit, | |||
visible: visibleProp = false, | |||
hasBrand = false, | |||
}) => { | |||
const [visible, setVisible] = React.useState<boolean>(); | |||
const autofocusRef = React.useRef<HTMLInputElement>(null); | |||
@@ -19,17 +24,17 @@ export const ContactCtaBanner: React.FC<ContactCtaBannerProps> = ({ | |||
const openContactForm: React.MouseEventHandler<HTMLAnchorElement> = (e) => { | |||
e.preventDefault(); | |||
setVisible(true); | |||
}; | |||
React.useEffect(() => { | |||
window.setTimeout(() => { | |||
autofocusRef.current?.focus(); | |||
if (messageRef.current && messageRef.current.style.height === '0px') { | |||
messageRef.current.style.height = '12rem'; | |||
} | |||
}); | |||
}; | |||
}, [visible]); | |||
React.useEffect(() => { | |||
setVisible(false); | |||
}, []); | |||
setVisible(visibleProp); | |||
}, [visibleProp]); | |||
return ( | |||
<div className="pt-8 sm:pt-16 pb-8 sm:pb-16 flex items-center relative"> | |||
@@ -40,6 +45,13 @@ export const ContactCtaBanner: React.FC<ContactCtaBannerProps> = ({ | |||
span="wide" | |||
className="relative" | |||
> | |||
{hasBrand && ( | |||
<h1 className="text-center mt-0 mb-16 before:text-7xl sm:before:text-8xl before:block before:content-['I_am.'] text-base leading-none"> | |||
<span className="text-4xl"> | |||
<Brand/> | |||
</span> | |||
</h1> | |||
)} | |||
<div className="flex flex-wrap flex-row items-center sm:justify-between gap-6 sm:gap-12"> | |||
<p className="m-0"> | |||
<strong | |||
@@ -85,6 +97,8 @@ export const ContactCtaBanner: React.FC<ContactCtaBannerProps> = ({ | |||
block | |||
border | |||
ref={autofocusRef} | |||
autoComplete="off" | |||
autoFocus | |||
/> | |||
</div> | |||
<div> | |||
@@ -104,6 +118,7 @@ export const ContactCtaBanner: React.FC<ContactCtaBannerProps> = ({ | |||
block | |||
border | |||
required | |||
rows={8} | |||
/> | |||
</div> | |||
<div className="col-span-2 text-right"> | |||
@@ -150,13 +150,12 @@ export const Footer = () => ( | |||
</dl> | |||
</div> | |||
<div className="text-center"> | |||
© | |||
{' '} | |||
<span className="text-2xl mx-3"> | |||
<span className="text-2xl mx-3 block mb-4"> | |||
<Brand/> | |||
</span> | |||
© | |||
{' '} | |||
{formatYear(2023, new Date().getFullYear())}. | |||
{formatYear(2023, new Date().getFullYear())} | |||
</div> | |||
</Layouts.Basic.ContentContainer> | |||
</footer> | |||
@@ -30,13 +30,6 @@ export const MainLandingSection: React.FC<MainLandingSectionProps> = ({ | |||
<div | |||
className="sm:py-24 flex items-center min-h-full relative overflow-hidden" | |||
> | |||
<div | |||
className="fixed top-0 left-0 w-full h-full" | |||
> | |||
<div className="w-[125%] h-[125%] absolute -top-[12.5%] -left-[12.5%] opacity-25"> | |||
<BackgroundGrid images={backgroundImages} /> | |||
</div> | |||
</div> | |||
<div | |||
className="absolute top-0 left-0 w-full h-full bg-shade opacity-25" | |||
/> | |||
@@ -46,9 +39,9 @@ export const MainLandingSection: React.FC<MainLandingSectionProps> = ({ | |||
> | |||
<div className="flex flex-col sm:flex-row sm:items-center gap-8 sm:gap-16"> | |||
<h1 className="text-center m-0 before:text-7xl sm:before:text-8xl before:block before:content-['I_am.'] text-base leading-none"> | |||
<span className="text-4xl"> | |||
<Brand/> | |||
</span> | |||
<span className="text-4xl"> | |||
<Brand/> | |||
</span> | |||
</h1> | |||
<div className="w-full sm:w-auto flex-auto flex flex-row sm:flex-col gap-2 h-64 sm:h-64"> | |||
<div className="w-0 sm:w-auto flex-auto"> | |||
@@ -6,13 +6,17 @@ import {EnvisionSection, EnvisionSectionProps} from '@/components/molecules/Envi | |||
import {Footer} from '@/components/molecules/Footer'; | |||
import {FeaturedProjectSection, FeaturedProjectSectionProps} from '@/components/molecules/FeaturedProjectSection'; | |||
import {ContactCtaBanner} from '@/components/molecules/ContactCtaBanner'; | |||
import {BackgroundGrid} from '@/components/molecules/BackgroundGrid'; | |||
export interface IndexLayoutProps { | |||
backgroundImages: MainLandingSectionProps['backgroundImages']; | |||
makeSectionData: MakeSectionProps['data']; | |||
envisionSectionData: EnvisionSectionProps['data']; | |||
makeSectionData?: MakeSectionProps['data']; | |||
envisionSectionData?: EnvisionSectionProps['data']; | |||
featuredProjectData?: FeaturedProjectSectionProps[]; | |||
onSubmit?: React.FormEventHandler<HTMLFormElement>; | |||
contactVisible?: boolean; | |||
hasMainLandingSection?: boolean; | |||
contactHasBrand?: boolean; | |||
} | |||
export const IndexLayout: React.FC<IndexLayoutProps> = ({ | |||
@@ -21,18 +25,36 @@ export const IndexLayout: React.FC<IndexLayoutProps> = ({ | |||
envisionSectionData, | |||
featuredProjectData = [], | |||
onSubmit, | |||
contactVisible = false, | |||
hasMainLandingSection = false, | |||
contactHasBrand = false, | |||
}) => ( | |||
<Layouts.Basic.Root className="contents"> | |||
<MainLandingSection | |||
backgroundImages={backgroundImages} | |||
/> | |||
<div id="start" /> | |||
<MakeSection | |||
data={makeSectionData} | |||
/> | |||
<EnvisionSection | |||
data={envisionSectionData} | |||
/> | |||
<div | |||
className="fixed top-0 left-0 w-full h-full" | |||
> | |||
<div className="w-[125%] h-[125%] absolute -top-[12.5%] -left-[12.5%] opacity-25"> | |||
<BackgroundGrid images={backgroundImages} /> | |||
</div> | |||
</div> | |||
{hasMainLandingSection && ( | |||
<> | |||
<MainLandingSection | |||
backgroundImages={backgroundImages} | |||
/> | |||
<div id="start" /> | |||
</> | |||
)} | |||
{Array.isArray(makeSectionData) && ( | |||
<MakeSection | |||
data={makeSectionData} | |||
/> | |||
)} | |||
{Array.isArray(envisionSectionData) && ( | |||
<EnvisionSection | |||
data={envisionSectionData} | |||
/> | |||
)} | |||
{featuredProjectData.map((featuredProjectProps) => ( | |||
<FeaturedProjectSection | |||
key={featuredProjectProps.title} | |||
@@ -41,6 +63,8 @@ export const IndexLayout: React.FC<IndexLayoutProps> = ({ | |||
))} | |||
<ContactCtaBanner | |||
onSubmit={onSubmit} | |||
visible={contactVisible} | |||
hasBrand={contactHasBrand} | |||
/> | |||
<Footer/> | |||
</Layouts.Basic.Root> | |||
@@ -0,0 +1,8 @@ | |||
export namespace effects { | |||
export namespace huePulsate { | |||
export const propertyName = '--color-primary'; | |||
export const initialColorHueDegrees = 320; | |||
export const colorSaturationPercentage = 35; | |||
export const colorLightnessPercentage = 66; | |||
} | |||
} |
@@ -0,0 +1,14 @@ | |||
import * as React from 'react'; | |||
import { getFormValues } from '@theoryofnekomata/formxtra'; | |||
export const useContactForm = () => { | |||
const processContactForm: React.FormEventHandler<HTMLFormElement> = (e) => { | |||
e.preventDefault(); | |||
const values = getFormValues(e.currentTarget); | |||
console.log(values); | |||
}; | |||
return React.useMemo(() => ({ | |||
processContactForm | |||
}), []); | |||
}; |
@@ -21,7 +21,7 @@ export interface UseHuePulsateOptions { | |||
propertyName: string; | |||
} | |||
export const useHuePulsate = (options : UseHuePulsateOptions) => { | |||
export const useHuePulsate = (options: UseHuePulsateOptions) => { | |||
React.useEffect(() => { | |||
const { | |||
animationDuration = DEFAULT_ANIMATION_DURATION, | |||
@@ -0,0 +1,35 @@ | |||
import {NextPage} from 'next'; | |||
import {useHuePulsate} from '@/hooks/effects'; | |||
import * as config from '@/config'; | |||
import * as React from 'react'; | |||
import {IndexLayout} from '@/components/organisms/IndexLayout'; | |||
const IMAGE_CHOICES = [ | |||
'/images/3cd237361eada7fd30eb96d42d55ec00.jpg', | |||
'/images/5ace16248237a96f6dbbcc16a3c385fe.jpg', | |||
'/images/6af0fdc5eafb86f8b540e3d322c3a007.jpg', | |||
'/images/8f5e3d92d2da0a760b312a9387219447.jpg', | |||
'/images/162cab44da8420c50900d8f11c6da6fd.jpg', | |||
'/images/2626c3485668a19730c64a7ee672a214.jpg', | |||
'/images/9802f260887e9044312fb7d88547fb09.jpg', | |||
'/images/a3a56f54a11c76f46f4392b748f3026a.png', | |||
'/images/a96d02d8cc43cc3b9f1e77c43dfa5644.png', | |||
'/images/d69ab7f4e5747b5b8f297e4ebe9770f5.png', | |||
'/images/d1454662da2a4a8e77cd4c98eda6d662.png', | |||
'/images/e4753cf6a55db6b7ab5037ed0fd4a3fe.jpg', | |||
'/images/fe3050a31d3fb86accf26cc4bebec102.png', | |||
]; | |||
const ContactPage: NextPage = () => { | |||
useHuePulsate(config.effects.huePulsate); | |||
return ( | |||
<IndexLayout | |||
backgroundImages={IMAGE_CHOICES} | |||
contactVisible | |||
contactHasBrand | |||
/> | |||
) | |||
}; | |||
export default ContactPage; |
@@ -1,8 +1,9 @@ | |||
import type { NextPage } from 'next'; | |||
import * as React from 'react'; | |||
import {useHuePulsate} from '@/hooks/effects'; | |||
import {useContactForm} from '@/hooks/contact'; | |||
import {IndexLayout} from '@/components/organisms/IndexLayout'; | |||
import {getFormValues} from '@theoryofnekomata/formxtra'; | |||
import * as config from '@/config'; | |||
const IMAGE_CHOICES = [ | |||
'/images/3cd237361eada7fd30eb96d42d55ec00.jpg', | |||
@@ -122,18 +123,9 @@ const featuredProjectData = [ | |||
]; | |||
const IndexPage: NextPage = () => { | |||
useHuePulsate({ | |||
propertyName: '--color-primary', | |||
initialColorHueDegrees: 320, | |||
colorSaturationPercentage: 35, | |||
colorLightnessPercentage: 66, | |||
}); | |||
useHuePulsate(config.effects.huePulsate); | |||
const processContactForm: React.FormEventHandler<HTMLFormElement> = (e) => { | |||
e.preventDefault(); | |||
const values = getFormValues(e.currentTarget); | |||
console.log(values); | |||
}; | |||
const { processContactForm } = useContactForm(); | |||
return ( | |||
<IndexLayout | |||
@@ -142,6 +134,7 @@ const IndexPage: NextPage = () => { | |||
envisionSectionData={envisionSectionData} | |||
featuredProjectData={featuredProjectData} | |||
onSubmit={processContactForm} | |||
hasMainLandingSection | |||
/> | |||
); | |||
}; | |||