Compare commits
No commits in common. "c6084228fc123241c69c6e3064a15be4dc5db8a3" and "920e83963c36d5c4a4c2a0b1f8f37f469509f799" have entirely different histories.
c6084228fc
...
920e83963c
@ -4,7 +4,7 @@
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"dev": "next dev --turbo",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint"
|
||||
|
||||
106
payload-types.ts
106
payload-types.ts
@ -587,98 +587,12 @@ export interface HomeHero {
|
||||
};
|
||||
[k: string]: unknown;
|
||||
};
|
||||
heroItems: {
|
||||
heroDataDesign: {
|
||||
root: {
|
||||
type: string;
|
||||
children: {
|
||||
type: string;
|
||||
version: number;
|
||||
[k: string]: unknown;
|
||||
}[];
|
||||
direction: ('ltr' | 'rtl') | null;
|
||||
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
|
||||
indent: number;
|
||||
version: number;
|
||||
};
|
||||
[k: string]: unknown;
|
||||
};
|
||||
heroDataCollection: {
|
||||
root: {
|
||||
type: string;
|
||||
children: {
|
||||
type: string;
|
||||
version: number;
|
||||
[k: string]: unknown;
|
||||
}[];
|
||||
direction: ('ltr' | 'rtl') | null;
|
||||
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
|
||||
indent: number;
|
||||
version: number;
|
||||
};
|
||||
[k: string]: unknown;
|
||||
};
|
||||
heroDataAnalysis: {
|
||||
root: {
|
||||
type: string;
|
||||
children: {
|
||||
type: string;
|
||||
version: number;
|
||||
[k: string]: unknown;
|
||||
}[];
|
||||
direction: ('ltr' | 'rtl') | null;
|
||||
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
|
||||
indent: number;
|
||||
version: number;
|
||||
};
|
||||
[k: string]: unknown;
|
||||
};
|
||||
heroDataVisualisation: {
|
||||
root: {
|
||||
type: string;
|
||||
children: {
|
||||
type: string;
|
||||
version: number;
|
||||
[k: string]: unknown;
|
||||
}[];
|
||||
direction: ('ltr' | 'rtl') | null;
|
||||
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
|
||||
indent: number;
|
||||
version: number;
|
||||
};
|
||||
[k: string]: unknown;
|
||||
};
|
||||
heroTraining: {
|
||||
root: {
|
||||
type: string;
|
||||
children: {
|
||||
type: string;
|
||||
version: number;
|
||||
[k: string]: unknown;
|
||||
}[];
|
||||
direction: ('ltr' | 'rtl') | null;
|
||||
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
|
||||
indent: number;
|
||||
version: number;
|
||||
};
|
||||
[k: string]: unknown;
|
||||
};
|
||||
heroDataSovereignty: {
|
||||
root: {
|
||||
type: string;
|
||||
children: {
|
||||
type: string;
|
||||
version: number;
|
||||
[k: string]: unknown;
|
||||
}[];
|
||||
direction: ('ltr' | 'rtl') | null;
|
||||
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
|
||||
indent: number;
|
||||
version: number;
|
||||
};
|
||||
[k: string]: unknown;
|
||||
};
|
||||
};
|
||||
heroItems?:
|
||||
| {
|
||||
name: string;
|
||||
id?: string | null;
|
||||
}[]
|
||||
| null;
|
||||
};
|
||||
/**
|
||||
* This title will be used for SEO purposes, and displayed in the browser tab.
|
||||
@ -744,12 +658,8 @@ export interface HomeHeroSelect<T extends boolean = true> {
|
||||
heroItems?:
|
||||
| T
|
||||
| {
|
||||
heroDataDesign?: T;
|
||||
heroDataCollection?: T;
|
||||
heroDataAnalysis?: T;
|
||||
heroDataVisualisation?: T;
|
||||
heroTraining?: T;
|
||||
heroDataSovereignty?: T;
|
||||
name?: T;
|
||||
id?: T;
|
||||
};
|
||||
};
|
||||
metaTitle?: T;
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 17 MiB |
@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { RichText } from "@payloadcms/richtext-lexical/react";
|
||||
import { motion, useScroll, useTransform } from "motion/react";
|
||||
import { motion, MotionValue, useScroll, useTransform } from "motion/react";
|
||||
import { useRef, useEffect, useState, useCallback } from "react";
|
||||
import * as d3 from "d3";
|
||||
|
||||
@ -34,11 +34,10 @@ export default function HeroIntro({
|
||||
|
||||
const drawBeam = useCallback(() => {
|
||||
if (!windowSize) return;
|
||||
const isSmall = windowSize[0] < 1124;
|
||||
const beam: [number, number][] = [
|
||||
[0, 1 - beamSize / 2], // modify over scroll
|
||||
[0.9, 0.1],
|
||||
[isSmall ? 0.7 : beamSize / 2, 1], // modify over scroll
|
||||
[0 + beamSize / 2, 1], // modify over scroll
|
||||
[0, 1],
|
||||
];
|
||||
|
||||
@ -54,7 +53,7 @@ export default function HeroIntro({
|
||||
}, [windowSize, beamSize]);
|
||||
|
||||
return (
|
||||
<section
|
||||
<div
|
||||
className="h-screen bg-black flex justify-center relative overflow-clip"
|
||||
ref={containerRef}
|
||||
>
|
||||
@ -70,10 +69,10 @@ export default function HeroIntro({
|
||||
</div>
|
||||
|
||||
<div className="w-full h-full max-w-6xl p-12 grid grid-cols-2 gap-12 z-10">
|
||||
<div className="absolute top-1/2 left-0 h-1/2 w-2/3 lg:w-1/2 flex-1 flex flex-col justify-center px-[5vw] gap-8 ">
|
||||
<div className="absolute top-1/2 left-0 h-1/2 w-1/2 flex-1 flex flex-col justify-center px-[5vw] gap-8">
|
||||
<motion.h2
|
||||
style={{ opacity: scrollYProgress, y: titleY }}
|
||||
className="text-3xl lg:text-5xl tracking-tight leading-tight font-display"
|
||||
className="text-5xl tracking-tight leading-tight"
|
||||
>
|
||||
{title}
|
||||
</motion.h2>
|
||||
@ -85,11 +84,11 @@ export default function HeroIntro({
|
||||
>
|
||||
<RichText
|
||||
data={desc}
|
||||
className="text-xl lg:text-2xl leading-tight"
|
||||
className="text-white text-2xl leading-tight"
|
||||
/>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,86 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { motion, useScroll, useTransform } from "motion/react";
|
||||
import { useRef } from "react";
|
||||
import type { HomeHero } from "@payload-types";
|
||||
import { RichText } from "@payloadcms/richtext-lexical/react";
|
||||
import useWindow from "@/app/(website)/hooks/useWindow";
|
||||
|
||||
const heroMap = {
|
||||
heroDataDesign: "Data Design",
|
||||
heroDataCollection: "Data Collection",
|
||||
heroDataAnalysis: "Data Analysis",
|
||||
heroDataVisualisation: "Data Visualisation",
|
||||
heroTraining: "Training",
|
||||
heroDataSovereignty: "Data Sovereignty",
|
||||
} as const;
|
||||
|
||||
type HeroItems = HomeHero["heroGroup"]["heroItems"];
|
||||
type HeroItem = HeroItems[keyof HeroItems];
|
||||
|
||||
export default function HeroData({
|
||||
items,
|
||||
}: {
|
||||
items: HomeHero["heroGroup"]["heroItems"];
|
||||
}) {
|
||||
const itemKeys: (keyof typeof items)[] = Object.keys(items) as any;
|
||||
const itemArray = itemKeys.map((k) => ({ key: k, ...items[k] }));
|
||||
|
||||
return (
|
||||
<section className="bg-black flex flex-col items-center relative">
|
||||
{itemArray.map((item) => (
|
||||
<Item key={item.key} title={heroMap[item.key]} item={item} />
|
||||
))}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
const Item = ({ title, item }: { title: string; item: HeroItem }) => {
|
||||
const { height } = useWindow();
|
||||
|
||||
const containerRef = useRef(null);
|
||||
const { scrollYProgress } = useScroll({
|
||||
target: containerRef,
|
||||
offset: ["start end", "end start"],
|
||||
});
|
||||
|
||||
const yp = 0.7;
|
||||
const yoffset = useTransform(
|
||||
scrollYProgress,
|
||||
[0.2, 0.8],
|
||||
[-height * yp, height * yp]
|
||||
);
|
||||
const opacity = useTransform(
|
||||
scrollYProgress,
|
||||
[0.3, 0.4, 0.6, 0.7],
|
||||
[0, 1, 1, 0]
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={containerRef}
|
||||
className="w-full h-screen max-w-6xl p-12 grid grid-cols-2 gap-12 z-10 items-center text-white"
|
||||
>
|
||||
<motion.div
|
||||
style={{ opacity }}
|
||||
className="flex items-center flex-col bg-accent-600 rounded aspect-video"
|
||||
>
|
||||
IMAGE
|
||||
</motion.div>
|
||||
<div className="relative">
|
||||
<motion.div
|
||||
style={{
|
||||
y: yoffset,
|
||||
opacity,
|
||||
}}
|
||||
className="flex flex-col gap-4 absolute top-1/2 -translate-y-1/2"
|
||||
>
|
||||
<h5 className="text-4xl font-display">{title}</h5>
|
||||
<div>
|
||||
<RichText className="text-xl" data={item} />
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@ -5,12 +5,12 @@ import { letters } from "./letters";
|
||||
|
||||
export default function ScrollingNumbers({ numbers }: { numbers: string[][] }) {
|
||||
return (
|
||||
<div className="absolute top-0 h-full w-full z-0 opacity-50 pointer-events-none">
|
||||
<div className="h-full w-full flex gap-4 skew-24 scale-150 overflow-hidden">
|
||||
<div className="absolute top-0 h-full w-full z-0 opacity-50 overflow-clip">
|
||||
<div className="h-full w-full flex gap-4 skew-24 scale-150">
|
||||
{numbers.map((col, i) => (
|
||||
<NumberCol col={col} key={i} />
|
||||
))}
|
||||
<div className="absolute w-full h-full mask-b-from-0 bg-black"></div>
|
||||
<div className="absolute w-full h-full mask-b-from-0 bg-black skew"></div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@ -34,7 +34,7 @@ const NumberCol = ({ col }: { col: string[] }) => {
|
||||
className="flex-1 flex flex-col items-center"
|
||||
>
|
||||
{col.map((num, j) => (
|
||||
<div key={j} className="text-accent-800 text-8xl">
|
||||
<div key={j} className="text-accent-600 text-3xl">
|
||||
{num}
|
||||
</div>
|
||||
))}
|
||||
|
||||
@ -2,15 +2,8 @@
|
||||
|
||||
import { ReactNode, useEffect } from "react";
|
||||
import Lenis from "lenis";
|
||||
import Snap from "lenis/snap";
|
||||
|
||||
export default function ({
|
||||
children,
|
||||
snapAt,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
snapAt?: string[];
|
||||
}) {
|
||||
export default function ({ children }: { children: ReactNode }) {
|
||||
useEffect(() => {
|
||||
const lenis = new Lenis();
|
||||
|
||||
@ -21,17 +14,6 @@ export default function ({
|
||||
}
|
||||
|
||||
requestAnimationFrame(raf);
|
||||
|
||||
// const snap = new Snap(lenis, {
|
||||
// type: "proximity",
|
||||
// velocityThreshold: 1,
|
||||
// debounce: 0,
|
||||
// });
|
||||
// snapAt?.forEach((id) => {
|
||||
// const el = document.querySelector<HTMLDivElement>(id);
|
||||
// if (!el) return;
|
||||
// snap.addElement(el, { align: ["center"] });
|
||||
// });
|
||||
}, []);
|
||||
return <>{children}</>;
|
||||
}
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
@import url("https://fonts.googleapis.com/css2?family=Inknut+Antiqua:wght@400;700&family=Work+Sans:ital,wght@0,100..900;1,100..900&display=swap");
|
||||
@import url("https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap");
|
||||
|
||||
@import "tailwindcss";
|
||||
|
||||
@theme {
|
||||
/* FONT FAMILIES */
|
||||
--font-sans: "Work Sans", sans;
|
||||
--font-display: "Inknut Antiqua";
|
||||
--font-sans: Inter, sans;
|
||||
--font-display: var(--font-sans);
|
||||
|
||||
/* COLOURS */
|
||||
--color-accent-50: #fff1f2;
|
||||
|
||||
@ -1,22 +0,0 @@
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
export default function useWindow() {
|
||||
const [width, setWidth] = useState(0);
|
||||
const [height, setHeight] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
if (!window) return;
|
||||
setWidth(window.innerWidth);
|
||||
setHeight(window.innerHeight);
|
||||
|
||||
window.addEventListener("resize", () => {
|
||||
setWidth(window.innerWidth);
|
||||
setHeight(window.innerHeight);
|
||||
});
|
||||
}, []);
|
||||
|
||||
return {
|
||||
width,
|
||||
height,
|
||||
};
|
||||
}
|
||||
@ -4,13 +4,9 @@ import ScrollingNumbers from "./components/Home/ScrollingNumbers";
|
||||
import { letters } from "./components/Home/letters";
|
||||
import HeroIntro from "./components/Home/Hero/01-intro";
|
||||
import SmoothScroll from "./components/SmoothScroll";
|
||||
import HeroData from "./components/Home/Hero/02-data";
|
||||
|
||||
import bgImage from "./bg.jpg";
|
||||
import Image from "next/image";
|
||||
|
||||
const randomNumbers = Array.from({ length: 10 }).map((i) =>
|
||||
Array.from({ length: 20 }).map(
|
||||
const randomNumbers = Array.from({ length: 50 }).map((i) =>
|
||||
Array.from({ length: 200 }).map(
|
||||
(j) => letters[Math.floor(Math.random() * letters.length)]
|
||||
)
|
||||
);
|
||||
@ -22,22 +18,16 @@ export default async function Home() {
|
||||
});
|
||||
|
||||
return (
|
||||
<SmoothScroll snapAt={["section"]}>
|
||||
<SmoothScroll>
|
||||
<div className="text-white">
|
||||
<div className="absolute h-full mt-[var(--header-height)] w-full opacity-50">
|
||||
<Image src={bgImage} alt="Bg image" objectFit="cover" />
|
||||
</div>
|
||||
<div className="h-screen pt-[var(--header-height)] flex flex-col items-center justify-end text-white pb-[10vh] relative">
|
||||
{/* <ScrollingNumbers numbers={randomNumbers} /> */}
|
||||
<h1 className="text-4xl p-8 lg:text-7xl leading-tight max-w-6xl z-10 font-display">
|
||||
{titleGroup.title}
|
||||
</h1>
|
||||
<ScrollingNumbers numbers={randomNumbers} />
|
||||
<h1 className="text-8xl max-w-6xl z-10">{titleGroup.title}</h1>
|
||||
</div>
|
||||
<HeroIntro
|
||||
title={heroGroup.heroTitle}
|
||||
desc={heroGroup.heroDescription}
|
||||
/>
|
||||
<HeroData items={heroGroup.heroItems} />
|
||||
</div>
|
||||
</SmoothScroll>
|
||||
);
|
||||
|
||||
@ -1,114 +1,83 @@
|
||||
import { GlobalConfig } from "payload";
|
||||
|
||||
export const HomeHero: GlobalConfig = {
|
||||
slug: "homeHero",
|
||||
label: "Hero",
|
||||
fields: [
|
||||
{
|
||||
name: "titleGroup",
|
||||
label: "Landing page",
|
||||
type: "group",
|
||||
fields: [
|
||||
slug: 'homeHero',
|
||||
label: 'Hero',
|
||||
fields: [
|
||||
{
|
||||
name: "title",
|
||||
label: "Title",
|
||||
type: "text",
|
||||
required: true,
|
||||
defaultValue:
|
||||
"Analytics, research, and data visualisation that make a difference",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "heroGroup",
|
||||
label: "Hero",
|
||||
type: "group",
|
||||
fields: [
|
||||
{
|
||||
name: "heroTitle",
|
||||
label: "Hero Title",
|
||||
type: "text",
|
||||
required: true,
|
||||
defaultValue: "We help people tell stories with data",
|
||||
name: 'titleGroup',
|
||||
label: 'Landing page',
|
||||
type: 'group',
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
label: 'Title',
|
||||
type: 'text',
|
||||
required: true,
|
||||
defaultValue: 'Analytics, research, and data visualisation that make a difference'
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "heroDescription",
|
||||
label: "Hero Description",
|
||||
type: "richText",
|
||||
required: true,
|
||||
name: 'heroGroup',
|
||||
label: 'Hero',
|
||||
type: 'group',
|
||||
fields: [
|
||||
{
|
||||
name: 'heroTitle',
|
||||
label: 'Hero Title',
|
||||
type: 'text',
|
||||
required: true,
|
||||
defaultValue: 'We help people tell stories with data'
|
||||
},
|
||||
{
|
||||
name: 'heroDescription',
|
||||
label: 'Hero Description',
|
||||
type: 'richText',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'heroItems',
|
||||
label: 'Items',
|
||||
type: 'array',
|
||||
fields: [
|
||||
{
|
||||
name: 'name',
|
||||
label: 'Name',
|
||||
type: 'text',
|
||||
required: true,
|
||||
}
|
||||
],
|
||||
defaultValue: ["Data design", "Data collection", "Data analysis", "Data visualisation", "Training", "Data sovereignty"].map((item) => ({
|
||||
name: item,
|
||||
})),
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "heroItems",
|
||||
label: "Items",
|
||||
type: "group",
|
||||
fields: [
|
||||
{
|
||||
name: "heroDataDesign",
|
||||
label: "Data Design",
|
||||
type: "richText",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: "heroDataCollection",
|
||||
label: "Data Collection",
|
||||
type: "richText",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: "heroDataAnalysis",
|
||||
label: "Data Analysis",
|
||||
type: "richText",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: "heroDataVisualisation",
|
||||
label: "Data Visualisation",
|
||||
type: "richText",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: "heroTraining",
|
||||
label: "Training",
|
||||
type: "richText",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: "heroDataSovereignty",
|
||||
label: "Data Sovereignty",
|
||||
type: "richText",
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
name: 'metaTitle',
|
||||
label: 'Meta Title',
|
||||
type: 'text',
|
||||
defaultValue: 'iNZight Analytics Ltd',
|
||||
required: true,
|
||||
admin: {
|
||||
position: 'sidebar',
|
||||
description: 'This title will be used for SEO purposes, and displayed in the browser tab.',
|
||||
}
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "metaTitle",
|
||||
label: "Meta Title",
|
||||
type: "text",
|
||||
defaultValue: "iNZight Analytics Ltd",
|
||||
required: true,
|
||||
admin: {
|
||||
position: "sidebar",
|
||||
description:
|
||||
"This title will be used for SEO purposes, and displayed in the browser tab.",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "metaDescription",
|
||||
label: "Meta Description",
|
||||
type: "textarea",
|
||||
required: true,
|
||||
defaultValue:
|
||||
"iNZight Analytics Ltd is a New Zealand-based company that provides data analysis and visualisation services.",
|
||||
admin: {
|
||||
position: "sidebar",
|
||||
description:
|
||||
"This description will be used for SEO purposes (e.g., shown in search results and on social media cards).",
|
||||
},
|
||||
},
|
||||
],
|
||||
admin: {
|
||||
group: "Home page",
|
||||
},
|
||||
{
|
||||
name: 'metaDescription',
|
||||
label: 'Meta Description',
|
||||
type: 'textarea',
|
||||
required: true,
|
||||
defaultValue: 'iNZight Analytics Ltd is a New Zealand-based company that provides data analysis and visualisation services.',
|
||||
admin: {
|
||||
position: 'sidebar',
|
||||
description: 'This description will be used for SEO purposes (e.g., shown in search results and on social media cards).',
|
||||
}
|
||||
},
|
||||
],
|
||||
admin: {
|
||||
group: 'Home page'
|
||||
}
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user