diff --git a/_data/pages/home.yml b/_data/pages/home.yml
index a3b5385ad5..1114e870b2 100644
--- a/_data/pages/home.yml
+++ b/_data/pages/home.yml
@@ -5,6 +5,12 @@ template: landing
breadcrumbs: false
page_last_updated: false
blocks:
+ - type: nav_sticky_banner
+ text: Starknet is the secure scaling technology bringing Ethereum’s benefits to
+ the world.
+ buttonText: See more
+ buttonLink: https://starkware.co
+ isActive: false
- type: group
blocks:
- type: home_hero
diff --git a/package.json b/package.json
index 1660e7b6c2..6da9f7b162 100644
--- a/package.json
+++ b/package.json
@@ -64,6 +64,7 @@
"highlight.js": "^11.8.0",
"highlightjs-cairo": "^0.2.0",
"itty-router": "^4.0.11",
+ "js-sha256": "^0.11.0",
"marked": "^5.1.0",
"moment": "^2.29.4",
"moment-timezone": "^0.5.43",
diff --git a/workspaces/cms-config/src/blocks.ts b/workspaces/cms-config/src/blocks.ts
index 993bd65ec8..9d06d6d221 100644
--- a/workspaces/cms-config/src/blocks.ts
+++ b/workspaces/cms-config/src/blocks.ts
@@ -56,7 +56,7 @@ export const ambassadorTags = [
label: "Tag",
name: "tag",
widget: "select",
- options: ["Content generator", "Event organizer", "Event speaker"]
+ options: ["Content generator", "Event organizer", "Event speaker"],
},
] satisfies CmsField[];
@@ -89,19 +89,19 @@ export const ambassador = [
label: "Website url",
name: "website",
widget: "string",
- crowdin: false
+ crowdin: false,
},
{
label: "Twitter handle",
name: "twitter",
widget: "string",
- crowdin: false
+ crowdin: false,
},
{
label: "Discord",
name: "discord",
widget: "string",
- crowdin: false
+ crowdin: false,
},
{
label: "Tags",
@@ -111,7 +111,7 @@ export const ambassador = [
crowdin: true,
required: false,
index_file: "",
- meta: true
+ meta: true,
},
] satisfies CmsField[];
@@ -141,7 +141,7 @@ export const cardListItem = [
required: false,
widget: "string",
index_file: "",
- meta: false
+ meta: false,
},
{
label: "Twitter",
@@ -149,7 +149,7 @@ export const cardListItem = [
required: false,
widget: "string",
index_file: "",
- meta: false
+ meta: false,
},
{
label: "Start date time",
@@ -157,7 +157,7 @@ export const cardListItem = [
required: false,
widget: "string",
index_file: "",
- meta: false
+ meta: false,
},
{
label: "Location",
@@ -165,7 +165,7 @@ export const cardListItem = [
required: false,
widget: "string",
index_file: "",
- meta: false
+ meta: false,
},
{
label: "City",
@@ -173,7 +173,7 @@ export const cardListItem = [
required: false,
widget: "string",
index_file: "",
- meta: false
+ meta: false,
},
{
label: "Country",
@@ -181,7 +181,7 @@ export const cardListItem = [
required: false,
widget: "string",
index_file: "",
- meta: false
+ meta: false,
},
{
label: "Discord",
@@ -189,7 +189,7 @@ export const cardListItem = [
required: false,
widget: "string",
index_file: "",
- meta: false
+ meta: false,
},
{
label: "Type list",
@@ -208,7 +208,7 @@ export const cardListItem = [
name: "url",
widget: "string",
crowdin: false,
- }
+ },
],
},
{
@@ -217,7 +217,7 @@ export const cardListItem = [
required: false,
widget: "string",
index_file: "",
- meta: false
+ meta: false,
},
{
label: "Recap",
@@ -242,35 +242,35 @@ export const cardListItem = [
name: "isExternal",
widget: "boolean",
crowdin: false,
- }
+ },
],
index_file: "",
- meta: false
+ meta: false,
},
] satisfies CmsField[];
const videoChapterFields = [
{
crowdin: true,
- label: 'Title',
- name: 'title',
+ label: "Title",
+ name: "title",
required: true,
- widget: 'string'
+ widget: "string",
},
{
crowdin: true,
- label: 'Description',
- name: 'description',
+ label: "Description",
+ name: "description",
required: true,
- widget: 'string'
+ widget: "string",
},
{
crowdin: true,
- label: 'Content',
- name: 'content',
+ label: "Content",
+ name: "content",
required: true,
- widget: 'markdown'
- }
+ widget: "markdown",
+ },
] satisfies CmsField[];
export const blocks = [
@@ -298,7 +298,7 @@ export const blocks = [
{
name: "description",
widget: "string",
- crowdin: true
+ crowdin: true,
},
],
},
@@ -332,7 +332,7 @@ export const blocks = [
{
name: "title",
widget: "string",
- crowdin: true
+ crowdin: true,
},
{
name: "link",
@@ -364,7 +364,7 @@ export const blocks = [
{ label: "Icon link card", value: "icon_link_card" },
{ label: "Dapp", value: "dapp" },
{ label: "Large card", value: "large_card" },
- { label: "Community card", value: "community_card" }
+ { label: "Community card", value: "community_card" },
],
},
{
@@ -375,7 +375,7 @@ export const blocks = [
required: false,
options: [
{ label: "Large", value: "large" },
- { label: "Small", value: "small" }
+ { label: "Small", value: "small" },
],
},
{
@@ -388,12 +388,12 @@ export const blocks = [
{
name: "title",
widget: "string",
- crowdin: true
+ crowdin: true,
},
{
name: "description",
widget: "string",
- crowdin: true
+ crowdin: true,
},
{
name: "link",
@@ -402,7 +402,7 @@ export const blocks = [
},
{
name: "icon",
- widget: "image"
+ widget: "image",
},
{
name: "color",
@@ -416,13 +416,14 @@ export const blocks = [
"cyan",
"orange",
"pink",
- "grey"
+ "grey",
],
default: "orange",
},
{
name: "columns",
- label: "Columns (number of cards per row, works only for icon_link_card)",
+ label:
+ "Columns (number of cards per row, works only for icon_link_card)",
widget: "select",
default: "4",
required: false,
@@ -435,7 +436,7 @@ export const blocks = [
default: "left",
required: false,
options: ["left", "right", "vertical"],
- }
+ },
],
},
{
@@ -446,9 +447,9 @@ export const blocks = [
{
name: "videoId",
widget: "string",
- crowdin: true
- }
- ]
+ crowdin: true,
+ },
+ ],
},
{
name: "ambassadors_list",
@@ -460,7 +461,7 @@ export const blocks = [
name: "title",
required: false,
widget: "string",
- crowdin: true
+ crowdin: true,
},
{
label: "Ambassador",
@@ -470,9 +471,9 @@ export const blocks = [
crowdin: true,
required: true,
index_file: "",
- meta: true
+ meta: true,
},
- ]
+ ],
},
{
name: "card_list",
@@ -484,14 +485,14 @@ export const blocks = [
name: "title",
required: false,
widget: "string",
- crowdin: true
+ crowdin: true,
},
{
label: "Description",
name: "description",
required: false,
widget: "string",
- crowdin: true
+ crowdin: true,
},
{
name: "randomize",
@@ -508,9 +509,9 @@ export const blocks = [
crowdin: true,
required: true,
index_file: "",
- meta: true
+ meta: true,
},
- ]
+ ],
},
{
name: "hero",
@@ -520,12 +521,12 @@ export const blocks = [
{
name: "title",
widget: "string",
- crowdin: true
+ crowdin: true,
},
{
name: "description",
widget: "string",
- crowdin: true
+ crowdin: true,
},
{
name: "variant",
@@ -549,21 +550,21 @@ export const blocks = [
label: "Button text",
required: false,
widget: "string",
- crowdin: true
+ crowdin: true,
},
{
name: "buttonUrl",
label: "Button url",
required: false,
widget: "string",
- crowdin: false
+ crowdin: false,
},
{
name: "leftBoxMaxWidth",
label: "Left box max width",
required: false,
widget: "number",
- crowdin: false
+ crowdin: false,
},
],
},
@@ -577,7 +578,7 @@ export const blocks = [
label: "Heading",
required: false,
widget: "string",
- crowdin: true
+ crowdin: true,
},
{
name: "listSize",
@@ -629,13 +630,13 @@ export const blocks = [
name: "label",
label: "Label",
widget: "string",
- crowdin: true
+ crowdin: true,
},
{
name: "boldLabel",
label: "Bold Label",
widget: "string",
- crowdin: true
+ crowdin: true,
},
],
},
@@ -648,13 +649,13 @@ export const blocks = [
name: "url",
label: "URL",
widget: "string",
- crowdin: false
+ crowdin: false,
},
{
name: "title",
label: "Title",
widget: "string",
- crowdin: true
+ crowdin: true,
},
{
name: "displayTitle",
@@ -678,7 +679,7 @@ export const blocks = [
label: "Heading",
required: false,
widget: "string",
- crowdin: true
+ crowdin: true,
},
{
name: "blocks",
@@ -689,7 +690,7 @@ export const blocks = [
name: "label",
label: "Label",
widget: "string",
- crowdin: true
+ crowdin: true,
},
{
name: "body",
@@ -699,7 +700,8 @@ export const blocks = [
],
},
],
- }, {
+ },
+ {
name: "video_section",
label: "Education video section",
widget: "object",
@@ -720,52 +722,54 @@ export const blocks = [
},
{
crowdin: true,
- label: 'Chapter 1',
- name: 'scaling-eth',
- widget: 'object',
- fields: videoChapterFields
+ label: "Chapter 1",
+ name: "scaling-eth",
+ widget: "object",
+ fields: videoChapterFields,
},
{
crowdin: true,
- label: 'Chapter 2',
- name: 'sequencer',
- widget: 'object',
- fields: videoChapterFields
+ label: "Chapter 2",
+ name: "sequencer",
+ widget: "object",
+ fields: videoChapterFields,
},
{
crowdin: true,
- label: 'Chapter 3',
- name: 'prover',
- widget: 'object',
- fields: videoChapterFields
+ label: "Chapter 3",
+ name: "prover",
+ widget: "object",
+ fields: videoChapterFields,
},
{
crowdin: true,
- label: 'Chapter 4',
- name: 'eth-settlement',
- widget: 'object',
- fields: videoChapterFields
- }
- ]
- }, {
+ label: "Chapter 4",
+ name: "eth-settlement",
+ widget: "object",
+ fields: videoChapterFields,
+ },
+ ],
+ },
+ {
name: "newsletter_popup",
label: "Newsletter Popup",
widget: "object",
fields: [
{
crowdin: true,
- label: 'Title',
- name: 'title',
- widget: 'string'
+ label: "Title",
+ name: "title",
+ widget: "string",
},
{
crowdin: true,
- label: 'Description',
- name: 'description',
- widget: 'string'
+ label: "Description",
+ name: "description",
+ widget: "string",
},
- ]
- }, {
+ ],
+ },
+ {
name: "ordered_block",
label: "Ordered Block",
widget: "object",
@@ -778,7 +782,7 @@ export const blocks = [
{
name: "title",
widget: "string",
- crowdin: true
+ crowdin: true,
},
{
name: "body",
@@ -789,6 +793,37 @@ export const blocks = [
},
],
},
+ {
+ name: "nav_sticky_banner",
+ label: "Sticky Banner Under Nav",
+ widget: "object",
+ fields: [
+ {
+ name: "text",
+ label: "Text",
+ widget: "string",
+ crowdin: true,
+ },
+ {
+ name: "buttonText",
+ label: "ButtonText",
+ widget: "string",
+ crowdin: true,
+ },
+ {
+ name: "buttonLink",
+ label: "buttonLink",
+ widget: "string",
+ crowdin: true,
+ },
+ {
+ name: "isActive",
+ label: "IsActive",
+ widget: "boolean",
+ default: true,
+ },
+ ],
+ },
] satisfies CmsFieldList["types"];
const flexLayout = {
@@ -820,7 +855,7 @@ const flexLayout = {
name: "heading",
required: false,
widget: "string",
- crowdin: true
+ crowdin: true,
},
{
name: "heading_variant",
@@ -882,15 +917,15 @@ export const topLevelBlocks = [
label: "Heading",
name: "heading",
widget: "string",
- crowdin: true
+ crowdin: true,
},
{
label: "Heading variant",
name: "heading_variant",
widget: "select",
options: ["h2", "h3", "h4", "h5", "h6"],
- default: 'h2',
- crowdin: false
+ default: "h2",
+ crowdin: false,
},
{
name: "blocks",
diff --git a/workspaces/cms-data/src/pages.ts b/workspaces/cms-data/src/pages.ts
index e156a25d72..f29c8c230c 100644
--- a/workspaces/cms-data/src/pages.ts
+++ b/workspaces/cms-data/src/pages.ts
@@ -1,6 +1,10 @@
import { LinkData } from "./settings/main-menu";
import { defaultLocale } from "./i18n/config";
-import { getFirst, getJSON, getShuffledArray } from "@starknet-io/cms-utils/src/index";
+import {
+ getFirst,
+ getJSON,
+ getShuffledArray,
+} from "@starknet-io/cms-utils/src/index";
import type { Meta } from "@starknet-io/cms-utils/src/index";
export interface MarkdownBlock {
@@ -133,6 +137,15 @@ export interface HomeHeroBlock {
readonly heroText: string;
};
}
+
+export interface NavStickyBannerBlock {
+ readonly type: "nav_sticky_banner";
+ readonly text: string;
+ readonly buttonText: string;
+ readonly buttonLink: string;
+ readonly isActive: boolean;
+}
+
export interface LinkListBlock {
readonly type: "link_list";
readonly heading?: string;
@@ -152,17 +165,17 @@ export interface OrderedBlock {
}
export interface ChapterInfo {
- content: MarkdownBlock['body'];
+ content: MarkdownBlock["body"];
subtitle: string;
title: string;
-};
+}
export interface VideoSectionBlock {
readonly type: "video_section";
- readonly 'scaling-eth': ChapterInfo;
+ readonly "scaling-eth": ChapterInfo;
readonly sequencer: ChapterInfo;
readonly prover: ChapterInfo;
- readonly 'eth-settlement': ChapterInfo;
+ readonly "eth-settlement": ChapterInfo;
readonly chapterDescriptionFullWidth: boolean;
readonly playlistOnBottom: boolean;
}
@@ -183,6 +196,7 @@ export type Block =
| YoutubeBlock
| HeroBlock
| HomeHeroBlock
+ | NavStickyBannerBlock
| LinkListBlock
| PageHeaderBlock
| AccordionBlock
@@ -218,7 +232,12 @@ export interface HeadingContainerBlock {
readonly blocks: readonly Block[];
}
-export type TopLevelBlock = Block | FlexLayoutBlock | GroupBlock | Container | HeadingContainerBlock;
+export type TopLevelBlock =
+ | Block
+ | FlexLayoutBlock
+ | GroupBlock
+ | Container
+ | HeadingContainerBlock;
export interface Page extends Meta {
readonly id: string;
@@ -238,20 +257,19 @@ export interface Page extends Meta {
}
const getPageWithRandomizedData = (data: Page): Page => {
- const randomizedData = {...data}
+ const randomizedData = { ...data };
randomizedData.blocks?.forEach((block: TopLevelBlock) => {
-
- if (block.type === 'link_list' && block.randomize) {
+ if (block.type === "link_list" && block.randomize) {
//@ts-expect-error
block.blocks = getShuffledArray(block.blocks || []);
- } else if (block.type === 'card_list' && block.randomize) {
+ } else if (block.type === "card_list" && block.randomize) {
//@ts-expect-error
block.card_list_items = getShuffledArray(block.card_list_items || []);
}
- })
+ });
- return randomizedData
-}
+ return randomizedData;
+};
export async function getPageBySlug(
slug: string,
locale: string,
@@ -265,7 +283,7 @@ export async function getPageBySlug(
)
);
- return getPageWithRandomizedData(data)
+ return getPageWithRandomizedData(data);
} catch (cause) {
throw new Error(`Page not found! ${slug}`, {
cause,
@@ -281,7 +299,8 @@ export async function getPageById(
try {
return await getFirst(
...[locale, defaultLocale].map(
- (value) => async () => getJSON("data/pages/" + value + "/" + id, context)
+ (value) => async () =>
+ getJSON("data/pages/" + value + "/" + id, context)
)
);
} catch (cause) {
diff --git a/workspaces/website/src/blocks/Block.tsx b/workspaces/website/src/blocks/Block.tsx
index e15714634a..431aaebca3 100644
--- a/workspaces/website/src/blocks/Block.tsx
+++ b/workspaces/website/src/blocks/Block.tsx
@@ -21,162 +21,193 @@ import { HeadingContainer } from "./HeadingContainer";
import VideoSectionBlock from "./VideoSectionBlock";
import { NewsletterCard } from "@ui/Card/NewsletterCard";
import { YoutubePlayer } from "@ui/YoutubePlayer/YoutubePlayer";
+import NavbarStickyBanner from "../pages/(components)/NavbarStickyBanner/NavbarStickyBanner";
+
+export enum BlockPlacements {
+ DEFAULT = "DEFAULT",
+ NAVBAR = "NAVBAR",
+}
interface Props {
+ placement?: BlockPlacements;
disallowH1?: boolean;
readonly block: TopLevelBlock;
env: {
CLOUDFLARE_RECAPTCHA_KEY: string;
- }
+ };
readonly locale: string;
}
-export function Block({ disallowH1, block, env, locale }: Props): JSX.Element | null {
- if (block.type === "basic_card") {
- return ;
- } else if (block.type === "container") {
- return (
-
- {block.blocks.map((block, i) => (
-
- ))}
-
- );
- } else if (block.type === "image_icon_link_card") {
- return ;
- } else if (block.type === "youtube") {
- return
- } else if (block.type === "newsletter_popup") {
- return ;
- } else if (block.type === "markdown") {
- return ;
- } else if (block.type === "ambassadors_list") {
- return ;
- } else if (block.type === "community_events") {
- return (
-
- );
- } else if (block.type === "flex_layout") {
- return (
-
- {block.blocks.map((block, i) => (
-
- ))}
-
- );
- } else if (block.type === "link_list") {
- return ;
- } else if (block.type === "accordion") {
- return (
-
- {block.blocks?.map((block, i) => (
-
-
-
- ))}
-
- );
- } else if (block.type === "ordered_block") {
- let blocks = Array.from(block.blocks || []).sort((a, b) => {
- return a.title.localeCompare(b.title);
- });
-
- return (
-
- {blocks.map((block: any, i: number) => {
+export function Block({
+ placement = BlockPlacements.DEFAULT,
+ disallowH1,
+ block,
+ env,
+ locale,
+}: Props): JSX.Element | null {
+ switch (placement) {
+ case BlockPlacements.DEFAULT:
+ switch (block.type) {
+ case "basic_card":
+ return ;
+
+ case "container":
+ return (
+
+ {block.blocks.map((block, i) => (
+
+ ))}
+
+ );
+
+ case "image_icon_link_card":
+ return ;
+
+ case "youtube":
+ return ;
+
+ case "newsletter_popup":
+ return ;
+
+ case "markdown":
+ return ;
+
+ case "ambassadors_list":
+ return ;
+
+ case "community_events":
+ return (
+
+ );
+
+ case "flex_layout":
+ return (
+
+ {block.blocks.map((block, i) => (
+
+ ))}
+
+ );
+
+ case "link_list":
+ return ;
+
+ case "accordion":
+ return (
+
+ {block.blocks?.map((block, i) => (
+
+
+
+ ))}
+
+ );
+
+ case "ordered_block":
+ let blocks = Array.from(block.blocks || []).sort((a, b) => {
+ return a.title.localeCompare(b.title);
+ });
+ return (
+
+ {blocks.map((block: any, i: number) => {
+ return (
+
+
+
+ );
+ })}
+
+ );
+
+ case "page_header":
+ return (
+
+ );
+
+ case "group":
+ return (
+
+ {block.blocks.map((block, i) => (
+
+ ))}
+
+ );
+
+ case "heading_container":
+ return (
+
+ {block.blocks.map((block, i) => (
+
+ ))}
+
+ );
+
+ case "hero":
+ return (
+
+ );
+
+ case "home_hero":
+ const pageContext = usePageContext();
+ const homeSEO = useAsync(["getBlockExplorers", locale], () =>
+ getHomeSEO(locale, pageContext.context)
+ );
+
+ return ;
+
+ case "card_list":
+ return (
+
+ );
+ case "video_section":
+ return ;
+ }
+ break;
+
+ //========================================
+
+ case BlockPlacements.NAVBAR:
+ switch (block.type) {
+ case "nav_sticky_banner":
+ if (!block.isActive) return null;
return (
-
-
-
+
);
- })}
-
- );
- } else if (block.type === "page_header") {
- return (
-
- );
- } else if (block.type === "group") {
- return (
-
- {block.blocks.map((block, i) => (
-
- ))}
-
- );
- }else if (block.type === "heading_container") {
- return (
-
- {block.blocks.map((block, i) => (
-
- ))}
-
- );
- } else if (block.type === "hero") {
- return (
-
- );
- } else if (block.type === "home_hero") {
- const pageContext = usePageContext();
- const homeSEO = useAsync(["getBlockExplorers", locale], () =>
- getHomeSEO(locale, pageContext.context)
- );
-
- return ;
- } else if (block.type === "card_list") {
- return (
-
- );
- } else if (block.type === "video_section") {
- return (
-
- )
- } else {
- // this will report type error if there is unhandled block.type
- block satisfies never;
+ }
+ break;
}
return null;
diff --git a/workspaces/website/src/components/ProvisionsPopup/CloseIcon/CloseIcon.tsx b/workspaces/website/src/components/Icons/CloseIcon/CloseIcon.tsx
similarity index 59%
rename from workspaces/website/src/components/ProvisionsPopup/CloseIcon/CloseIcon.tsx
rename to workspaces/website/src/components/Icons/CloseIcon/CloseIcon.tsx
index 63f4279afb..121413eafb 100644
--- a/workspaces/website/src/components/ProvisionsPopup/CloseIcon/CloseIcon.tsx
+++ b/workspaces/website/src/components/Icons/CloseIcon/CloseIcon.tsx
@@ -7,18 +7,18 @@ const CloseIcon = () => {
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
-
+
-
-
+
+
diff --git a/workspaces/website/src/components/Layout/Navbar/NavLayout.tsx b/workspaces/website/src/components/Layout/Navbar/NavLayout.tsx
index a6fe83aaf8..5c131fd6e6 100644
--- a/workspaces/website/src/components/Layout/Navbar/NavLayout.tsx
+++ b/workspaces/website/src/components/Layout/Navbar/NavLayout.tsx
@@ -35,13 +35,17 @@ export const NavLayout = (props: NavLayoutProps) => {
toggleColorMode();
if (typeof window !== "undefined" && window.gtag) {
window.gtag("event", "theme_change", {
- 'event_category': "engagement",
- 'value': colorMode
+ event_category: "engagement",
+ value: colorMode,
});
}
- }
+ };
return (
-
+
@@ -55,46 +59,58 @@ export const NavLayout = (props: NavLayoutProps) => {
{props.searchArea}
-
-
- ) : (
-
- )
- }
- aria-label="Toggle color mode"
- onClick={toogleTheme}
- marginInlineStart="0 !important"
- />
-
-
- {!!props.languageSwitcher && (
- <>
-
-
- {props.languageSwitcher}
-
- >
- )}
+
+
+ ) : (
+
+ )
+ }
+ aria-label="Toggle color mode"
+ onClick={toogleTheme}
+ marginInlineStart="0 !important"
+ />
+
-
- }
- aria-label="Open Menu"
- onClick={onClickMenu}
- marginInlineStart={{ base: "0px !important", md: "12px !important" }}
+ {!!props.languageSwitcher && (
+ <>
+
-
+
+ {props.languageSwitcher}
+
+ >
+ )}
+
+
+ }
+ aria-label="Open Menu"
+ onClick={onClickMenu}
+ marginInlineStart={{
+ base: "0px !important",
+ md: "12px !important",
+ }}
+ />
+
diff --git a/workspaces/website/src/components/Layout/Navbar/Navbar.tsx b/workspaces/website/src/components/Layout/Navbar/Navbar.tsx
index 0a2363a85e..d0eb3017ff 100644
--- a/workspaces/website/src/components/Layout/Navbar/Navbar.tsx
+++ b/workspaces/website/src/components/Layout/Navbar/Navbar.tsx
@@ -23,7 +23,7 @@ type Props = {
mobileNavItems?: React.ReactNode;
languageSwitcher?: React.ReactNode;
search?: React.ReactNode;
- seo: SEOTexts['language'];
+ seo: SEOTexts["language"];
};
declare global {
@@ -37,7 +37,7 @@ export const NavBar = ({
mobileNavItems,
languageSwitcher,
search,
- seo
+ seo,
}: Props) => {
const { isOpen, onOpen, onClose } = useDisclosure();
const menuButtonRef = React.useRef(null);
@@ -52,11 +52,11 @@ export const NavBar = ({
toggleColorMode();
if (typeof window !== "undefined" && window.gtag) {
window.gtag("event", "theme_change", {
- 'event_category': "engagement",
- 'value': colorMode
+ event_category: "engagement",
+ value: colorMode,
});
}
- }
+ };
return (
diff --git a/workspaces/website/src/components/ProvisionsPopup/ProvisionsPopup.tsx b/workspaces/website/src/components/ProvisionsPopup/ProvisionsPopup.tsx
index d27cc88672..f73bd6546e 100644
--- a/workspaces/website/src/components/ProvisionsPopup/ProvisionsPopup.tsx
+++ b/workspaces/website/src/components/ProvisionsPopup/ProvisionsPopup.tsx
@@ -1,7 +1,7 @@
import { Box, Image, Icon, Fade, IconButton } from "@chakra-ui/react";
import Background from "./popup-background.png";
import Logo from "./popup-text.svg";
-import CloseIcon from "./CloseIcon/CloseIcon";
+import CloseIcon from "@ui/Icons/CloseIcon/CloseIcon";
import ArrowRight from "./ArrowRight/ArrowRight";
import { Button } from "@ui/Button";
import { useLocalStorage } from "usehooks-ts";
@@ -29,7 +29,7 @@ const ProvisionsPopup = () => {
setIsOpenStorage(false);
};
- if (!isOpenStorage) return;
+ if (!isOpenStorage) return null;
return (
{
top="10px"
width="28px"
height="28px"
- style={{ backgroundColor: "transparent" }}
+ bgColor="transparent"
+ _dark={{ bgColor: "transparent" }}
onClick={onClose}
>
diff --git a/workspaces/website/src/pages/(components)/CMSPage.tsx b/workspaces/website/src/pages/(components)/CMSPage.tsx
index 100e6dfaa6..90dee1cae8 100644
--- a/workspaces/website/src/pages/(components)/CMSPage.tsx
+++ b/workspaces/website/src/pages/(components)/CMSPage.tsx
@@ -10,24 +10,19 @@ import {
BreadcrumbItem,
BreadcrumbLink,
Flex,
- Divider
+ Divider,
} from "@chakra-ui/react";
-import '@ui/CodeHighlight/code-highlight-init'
+import "@ui/CodeHighlight/code-highlight-init";
import { blocksToTOC } from "./TableOfContents/blocksToTOC";
import NotFound from "@ui/NotFound/NotFound";
-
type CMSPageProps = {
data: PageType;
env: {
CLOUDFLARE_RECAPTCHA_KEY: string;
- }
+ };
locale: string;
};
-export default function CMSPage({
- data,
- env,
- locale,
-}: CMSPageProps) {
+export default function CMSPage({ data, env, locale }: CMSPageProps) {
const date = data?.gitlog?.date;
if (data?.hidden_page) {
@@ -60,7 +55,6 @@ export default function CMSPage({
{data.breadcrumbs_data[0].title}
-
{data.title}
@@ -77,38 +71,46 @@ export default function CMSPage({
- {data.show_title ? <>
-
-
- {data.title}
-
-
-
- > : null}
+ {data.show_title ? (
+ <>
+
+
+ {data.title}
+
+
+
+ >
+ ) : null}
{data.blocks?.map((block, i) => {
- return (
-
- );
+ return ;
})}
}
rightAside={
data.template === "content" && !data.hideToc ? (
-
+
) : null
}
/>
diff --git a/workspaces/website/src/pages/(components)/ClientOnly.tsx b/workspaces/website/src/pages/(components)/ClientOnly.tsx
new file mode 100644
index 0000000000..ab08d7e218
--- /dev/null
+++ b/workspaces/website/src/pages/(components)/ClientOnly.tsx
@@ -0,0 +1,14 @@
+// @see https://vite-plugin-ssr.com/client-only-components#react
+import React, { useEffect, useState } from "react";
+
+const ClientOnly = ({ children }: { children: React.ReactNode }) => {
+ const [isMounted, setIsMounted] = useState(false);
+
+ useEffect(() => {
+ setIsMounted(true);
+ }, []);
+
+ return isMounted ? <>{children}> : null;
+};
+
+export default ClientOnly;
diff --git a/workspaces/website/src/pages/(components)/Navbar.tsx b/workspaces/website/src/pages/(components)/Navbar.tsx
index bf7d63f10a..5bccfafbd5 100644
--- a/workspaces/website/src/pages/(components)/Navbar.tsx
+++ b/workspaces/website/src/pages/(components)/Navbar.tsx
@@ -1,4 +1,3 @@
-
import * as NavAccordian from "@ui/Layout/Navbar/NavAccordion";
import type { MainMenu } from "@starknet-io/cms-data/src/settings/main-menu";
import { NavBar } from "@ui/Layout/Navbar/Navbar";
@@ -6,15 +5,16 @@ import { MenuItemWithDropdown } from "@ui/Layout/Navbar/MenuItemWithDropdown";
import { NavbarContainer } from "@ui/Layout/Navbar/NavbarContainer";
import { NavBarLink } from "@ui/Layout/Navbar/NavBarLink";
import { NavbarHeading } from "@ui/Layout/Navbar/NavbarHeading";
-import { Flex } from "@chakra-ui/react";
+import { Box, ButtonGroup, Flex } from "@chakra-ui/react";
import { getComputedLinkData } from "src/utils/utils";
import { MainSearch } from "./MainSearch";
import React, { Fragment } from "react";
-import { Box, ButtonGroup } from "@chakra-ui/react";
import { IconButton } from "@ui/IconButton";
import { SiDiscord, SiGithub, SiTwitter, SiYoutube } from "react-icons/si";
import { SEOTexts } from "@starknet-io/cms-data/src/seo";
import { usePageContext } from "src/renderer/PageContextProvider";
+import type { TopLevelBlock } from "@starknet-io/cms-data/src/pages";
+import { Block, BlockPlacements } from "../../blocks/Block";
export interface Props {
readonly mainMenu: MainMenu;
@@ -22,9 +22,11 @@ export interface Props {
readonly ALGOLIA_INDEX: string;
readonly ALGOLIA_APP_ID: string;
readonly ALGOLIA_SEARCH_API_KEY: string;
+ readonly CLOUDFLARE_RECAPTCHA_KEY: string;
};
- readonly searchSEO: SEOTexts['search'];
- readonly languageCenterSeo: SEOTexts['language'];
+ readonly searchSEO: SEOTexts["search"];
+ readonly languageCenterSeo: SEOTexts["language"];
+ readonly pageBlocks?: TopLevelBlock[];
}
export default function Navbar({
@@ -32,6 +34,7 @@ export default function Navbar({
env,
searchSEO,
languageCenterSeo,
+ pageBlocks,
}: Props) {
const { locale, urlPathname: pathname } = usePageContext();
@@ -88,7 +91,9 @@ export default function Navbar({
) : item.custom_icon === "SiTwitter" ? (
) : item.custom_icon === "SiYoutube" ? (
-
+
+
+
) : (
)
@@ -187,6 +192,17 @@ export default function Navbar({
}
/>
+ {pageBlocks?.map((block, i) => {
+ return (
+
+ );
+ })}
);
}
diff --git a/workspaces/website/src/pages/(components)/NavbarStickyBanner/NavbarStickyBanner.tsx b/workspaces/website/src/pages/(components)/NavbarStickyBanner/NavbarStickyBanner.tsx
new file mode 100644
index 0000000000..c39d807510
--- /dev/null
+++ b/workspaces/website/src/pages/(components)/NavbarStickyBanner/NavbarStickyBanner.tsx
@@ -0,0 +1,104 @@
+import { Center, Text, IconButton } from "@chakra-ui/react";
+import { Button } from "@ui/Button";
+import CloseIcon from "@ui/Icons/CloseIcon/CloseIcon";
+import { useMemo } from "react";
+import { useLocalStorage } from "usehooks-ts";
+import { sha256 } from "js-sha256";
+import ClientOnly from "../ClientOnly";
+
+interface NavbarStickyBannerProps {
+ text: string;
+ buttonText: string;
+ buttonLink: string;
+}
+
+const NavbarStickyBanner = ({
+ text,
+ buttonText,
+ buttonLink,
+}: NavbarStickyBannerProps) => {
+ const hash = useMemo(
+ () => sha256(text + buttonLink + buttonText).slice(-8),
+ [text, buttonLink, buttonText]
+ );
+
+ const [isOpen, setIsOpenStorage] = useLocalStorage(
+ `isNavbarStickyBannerOpen_${hash}`,
+ true
+ );
+
+ const gtmEvent = (target: string) =>
+ window.gtag?.("event", target, { event_category: "engagement" });
+
+ const onClose = () => {
+ gtmEvent("Navbar_banner_close");
+ setIsOpenStorage(false);
+ };
+
+ const onReadMore = () => gtmEvent("Navbar_banner_read_more");
+
+ return (
+
+
+
+
+ {text}
+
+
+
+
+
+
+
+
+ );
+};
+
+export default NavbarStickyBanner;
diff --git a/workspaces/website/src/renderer/PageShell.tsx b/workspaces/website/src/renderer/PageShell.tsx
index 2490e9b09b..ad0c5392e3 100644
--- a/workspaces/website/src/renderer/PageShell.tsx
+++ b/workspaces/website/src/renderer/PageShell.tsx
@@ -12,6 +12,7 @@ import { ThemeProvider } from "./ThemeProvider";
import { PageContainer } from "src/pages/(components)/PageContainer";
import Navbar from "src/pages/(components)/Navbar";
import { Footer } from "src/pages/(components)/Footer";
+import { TopLevelBlock } from "@starknet-io/cms-data/src/pages";
interface Props {
readonly pageContext: PageContext;
@@ -58,9 +59,12 @@ function PageLayout(props: Props) {
ALGOLIA_APP_ID: import.meta.env.VITE_ALGOLIA_APP_ID!,
ALGOLIA_SEARCH_API_KEY: import.meta.env
.VITE_ALGOLIA_SEARCH_API_KEY!,
+ CLOUDFLARE_RECAPTCHA_KEY: import.meta.env
+ .VITE_CLOUDFLARE_RECAPTCHA_KEY!,
}}
searchSEO={pageContext.seo?.search}
languageCenterSeo={pageContext.seo?.language}
+ pageBlocks={pageContext.pageProps?.data?.blocks as TopLevelBlock[]}
/>
{children}
diff --git a/workspaces/website/src/renderer/types.ts b/workspaces/website/src/renderer/types.ts
index acb711b577..3b3806b316 100644
--- a/workspaces/website/src/renderer/types.ts
+++ b/workspaces/website/src/renderer/types.ts
@@ -11,10 +11,19 @@ import type {
PageContextBuiltInClientWithServerRouting as PageContextBuiltInClient
//*/
} from "vite-plugin-ssr/types";
+import { TopLevelBlock } from "@starknet-io/cms-data/src/pages";
type Page = (pageProps: PageProps) => React.ReactElement;
-export type PageProps = Record;
+interface DeepNestedObject {
+ [key: string]: T | DeepNestedObject;
+}
+
+export type PageProps = DeepNestedObject & {
+ data?: DeepNestedObject & {
+ blocks: TopLevelBlock[];
+ };
+};
export interface DocumentProps {
title?: string;
@@ -28,7 +37,7 @@ export type SeoType = {
seoCanonicalUrl?: string;
seoDescription?: string;
seoFocusKeywords?: string[];
-}
+};
export type PageContextCustom = {
Page: Page;
diff --git a/workspaces/website/src/style/theme.ts b/workspaces/website/src/style/theme.ts
index 542d0d4a07..748bc1859c 100644
--- a/workspaces/website/src/style/theme.ts
+++ b/workspaces/website/src/style/theme.ts
@@ -31,16 +31,16 @@ const theme = extendTheme(proTheme, {
100: "#80DCDA",
200: "#6DDAF5",
300: "#4B9EDA",
- 400: "#261F63"
+ 400: "#261F63",
},
orange: {
sunfade: "#FF7E6D",
- fanta: "#FF6450"
+ fanta: "#FF6450",
},
yellowSunfade: "#FFCD9A",
bg: {
main: "#F6F6F6",
- 200: "#FBFBFB"
+ 200: "#FBFBFB",
},
grey: {
morning: "#CCCCCC",
@@ -49,41 +49,42 @@ const theme = extendTheme(proTheme, {
coolerText: "#6B7280",
coolText: "#6B7280",
darkText: "#363636",
- lineOnDark: "#313131"
+ lineOnDark: "#313131",
},
darkMode: {
card: "#1B1B1B",
bg2: "#0B0B0B",
- navGrey: "#121212"
+ navGrey: "#121212",
},
snNavy: "#0C0C4F",
selected: {
main: "#5C94FF",
100: "#AFCAFF",
200: "#9EBFFF",
- 300: "#B2CDFF"
+ 300: "#B2CDFF",
},
notify: {
dark: "#172726",
green1: "#EFFBFA",
- green2: "#C4E2E0"
+ green2: "#C4E2E0",
},
note: {
dark: "#171C27",
main: "#4B9EDA",
100: "#EBF2FF",
- 200: "#DEE3ED"
+ 200: "#DEE3ED",
},
important: {
dark: "#231F1A",
100: "#FFF5EB",
- 200: "#E9E1DA"
+ 200: "#E9E1DA",
},
warning: {
dark: "#231B1A",
100: "#FFEDEB",
- 200: "#E7D5D4"
- }
+ 200: "#E7D5D4",
+ },
+ surfaceAccent: "#A4A4EA",
},
components: {
Accordion: accordionTheme,
@@ -110,34 +111,35 @@ const theme = extendTheme(proTheme, {
Link: linkTheme,
Heading: {
baseStyle: {
- fontWeight: "300"
+ fontWeight: "300",
},
variants: {
- ...headingTheme
+ ...headingTheme,
},
- sizes: null},
+ sizes: null,
+ },
Text: textTheme,
- Alert: alertTheme
+ Alert: alertTheme,
},
fonts: {
heading: "Inter, sans-serif",
},
fontSizes: {
- "h1": "5rem",
- "h2": "3rem",
- "h3": "1.5rem",
- "h4": "1.125rem",
- "h5": "1rem",
- "h6": "0.875rem",
+ h1: "5rem",
+ h2: "3rem",
+ h3: "1.5rem",
+ h4: "1.125rem",
+ h5: "1rem",
+ h6: "0.875rem",
},
lineHeights: {
- "h1": "100%",
- "h2": "3.625rem",
- "h3": "100%",
- "h4": "1.375rem",
- "h5": "1.188rem",
- "h6": "1.063rem",
- "heading6": "1.063rem",
+ h1: "100%",
+ h2: "3.625rem",
+ h3: "100%",
+ h4: "1.375rem",
+ h5: "1.188rem",
+ h6: "1.063rem",
+ heading6: "1.063rem",
},
breakpoints: {
xs: "24em",
@@ -145,8 +147,7 @@ const theme = extendTheme(proTheme, {
md: "48em",
lg: "62em",
xl: "80em",
- "2xl": "96em"
+ "2xl": "96em",
},
-
});
export default theme;
diff --git a/yarn.lock b/yarn.lock
index aa134bfe2f..05bdd13ca6 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4804,6 +4804,7 @@ __metadata:
highlight.js: ^11.8.0
highlightjs-cairo: ^0.2.0
itty-router: ^4.0.11
+ js-sha256: ^0.11.0
marked: ^5.1.0
moment: ^2.29.4
moment-timezone: ^0.5.43
@@ -12149,6 +12150,13 @@ __metadata:
languageName: node
linkType: hard
+"js-sha256@npm:^0.11.0":
+ version: 0.11.0
+ resolution: "js-sha256@npm:0.11.0"
+ checksum: 742d34a0c6eb15247309f1c74889b5a51df01a96e4307375b420fbe973f2f25585012b4d3c8fa52a1b18546153d12587e6463bb725dc1bc58686da03e892c334
+ languageName: node
+ linkType: hard
+
"js-sha256@npm:^0.9.0":
version: 0.9.0
resolution: "js-sha256@npm:0.9.0"