diff --git a/src/components/linkButton/LinkButton.tsx b/src/components/linkButton/LinkButton.tsx index 47d1b9def..191e4dcb3 100644 --- a/src/components/linkButton/LinkButton.tsx +++ b/src/components/linkButton/LinkButton.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import Link from "next/link"; import { getHref } from "src/utils/link"; import { ILink } from "studio/lib/interfaces/navigation"; @@ -25,9 +25,9 @@ const LinkButton = ({ isSmall, type = "primary", link }: IButton) => { return ( href && linkTitleValue && ( - + {linkTitleValue} - + ) ); }; diff --git a/src/components/sections/image-split/ImageSplit.tsx b/src/components/sections/image-split/ImageSplit.tsx new file mode 100644 index 000000000..c1f038b70 --- /dev/null +++ b/src/components/sections/image-split/ImageSplit.tsx @@ -0,0 +1,59 @@ +import { SanityImage } from "src/components/image/SanityImage"; +import LinkButton from "src/components/linkButton/LinkButton"; +import Text from "src/components/text/Text"; +import { ImageSplitSection } from "studio/lib/interfaces/pages"; +import { ImageAlignment } from "studio/schemas/fields/media"; + +import styles from "./image-split.module.css"; + +interface ImageSplitProps { + section: ImageSplitSection; +} + +const ImageSplitComponent = ({ section }: ImageSplitProps) => { + const hasImage = section.imageExtended; + const alignment = section.imageExtended?.imageAlignment; + const showImageToLeft = hasImage && alignment == ImageAlignment.Left; + const showImageToRight = hasImage && alignment == ImageAlignment.Right; + + return ( +
+ {showImageToLeft && ( +
+ +
+ )} + +
+ + {section.basicTitle} + + + {section.description && ( + {section.description} + )} + + {section.actions.length > 0 && ( +
+ {section.actions.map((action, index) => ( + + ))} +
+ )} +
+ + {showImageToRight && ( +
+ +
+ )} +
+ ); +}; + +export default ImageSplitComponent; diff --git a/src/components/sections/image-split/ImageSplitPreview.tsx b/src/components/sections/image-split/ImageSplitPreview.tsx new file mode 100644 index 000000000..ad36e3e67 --- /dev/null +++ b/src/components/sections/image-split/ImageSplitPreview.tsx @@ -0,0 +1,36 @@ +"use client"; +import { useQuery } from "@sanity/react-loader"; +import { Suspense } from "react"; + +import { PreviewProps } from "src/types/preview"; +import { ImageSplitSection, PageBuilder } from "studio/lib/interfaces/pages"; +import { PAGE_QUERY } from "studio/lib/queries/pages"; + +import ImageSplit from "./ImageSplit"; + +export default function ImageSplitComponentPreview({ + sectionIndex, + initialData, +}: PreviewProps) { + const { data: newData } = useQuery( + PAGE_QUERY, + { id: initialData.data._id, language: initialData.data.language }, + { initial: initialData }, + ); + + const imageSplitSection = newData + ? (newData.sections.find( + (section, index) => + section._type === "imageSplitSection" && index === sectionIndex, + ) as ImageSplitSection) + : (initialData.data.sections.find( + (section, index) => + section._type === "imageSplitSection" && index === sectionIndex, + ) as ImageSplitSection); + + return ( + + + + ); +} diff --git a/src/components/sections/image-split/image-split.module.css b/src/components/sections/image-split/image-split.module.css new file mode 100644 index 000000000..7537f129e --- /dev/null +++ b/src/components/sections/image-split/image-split.module.css @@ -0,0 +1,32 @@ +.imageSplit { + max-width: var(--max-content-width-small); + + display: grid; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + + gap: 3rem; + row-gap: 5.25rem; + padding: 1rem; + margin: 3.75rem auto; + + box-sizing: content-box; + align-items: center; +} + +.textContainer { + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.textContainer__link { + margin-top: 1.5rem; + display: flex; + flex-wrap: wrap; + gap: 0.5rem; +} + +.image { + display: flex; + justify-content: center; +} diff --git a/src/utils/renderSection.tsx b/src/utils/renderSection.tsx index 03e8d40ba..dfbed933d 100644 --- a/src/utils/renderSection.tsx +++ b/src/utils/renderSection.tsx @@ -1,5 +1,4 @@ import { QueryResponseInitial } from "@sanity/react-loader"; -import React from "react"; import Article from "src/components/sections/article/Article"; import ArticlePreview from "src/components/sections/article/ArticlePreview"; @@ -13,6 +12,8 @@ import Grid from "src/components/sections/grid/Grid"; import GridPreview from "src/components/sections/grid/GridPreview"; import { Hero } from "src/components/sections/hero/Hero"; import HeroPreview from "src/components/sections/hero/HeroPreview"; +import ImageSplitComponent from "src/components/sections/image-split/ImageSplit"; +import ImageSplitComponentPreview from "src/components/sections/image-split/ImageSplitPreview"; import ImageSectionComponent from "src/components/sections/imageSection/ImageSectionComponent"; import ImageSectionComponentPreview from "src/components/sections/imageSection/ImageSectionComponentPreview"; import { LogoSalad } from "src/components/sections/logoSalad/LogoSalad"; @@ -26,6 +27,7 @@ import { GridSection, HeroSection, ImageSection, + ImageSplitSection, LogoSaladSection, PageBuilder, Section, @@ -145,6 +147,26 @@ const renderImageSection = ( ); }; +function ImageSplitSectionWrapper({ + section, + sectionIndex, + isDraftMode, + initialData, +}: { + section: ImageSplitSection; + sectionIndex: number; + isDraftMode: boolean; + initialData: QueryResponseInitial; +}) { + return isDraftMode ? ( + + ) : ( + + ); +} const renderGridSection = ( section: GridSection, @@ -218,6 +240,15 @@ const SectionRenderer = ({ isDraftMode, initialData, ); + case "imageSplitSection": + return ( + + ); case "grid": return renderGridSection(section, sectionIndex, isDraftMode, initialData); case "contactBox": diff --git a/studio/lib/interfaces/pages.ts b/studio/lib/interfaces/pages.ts index 21b745204..5e65e52a6 100644 --- a/studio/lib/interfaces/pages.ts +++ b/studio/lib/interfaces/pages.ts @@ -68,6 +68,15 @@ export interface ImageSection { image: IImage; } +export interface ImageSplitSection { + _type: "imageSplitSection"; + _key: string; + basicTitle: string; + imageExtended: ImageExtendedProps; + description: string; + actions: ILink[]; +} + export interface GridSection { _type: "grid"; _key: string; @@ -108,6 +117,7 @@ export type Section = | CallToActionSection | TestimonialsSection | ImageSection + | ImageSplitSection | GridSection | ContactBoxSection | EmployeesSection; diff --git a/studio/lib/queries/pages.ts b/studio/lib/queries/pages.ts index 08f87533f..bd3cc2d17 100644 --- a/studio/lib/queries/pages.ts +++ b/studio/lib/queries/pages.ts @@ -27,6 +27,15 @@ const SECTIONS_FRAGMENT = groq` ${TRANSLATED_LINK_FRAGMENT} } }, + _type == "imageSplitSection" => { + ..., + "basicTitle": ${translatedFieldFragment("basicTitle")}, + "description": ${translatedFieldFragment("description")}, + actions[] { + ..., + ${TRANSLATED_LINK_FRAGMENT} + } + }, _type == "ctaSection" => { ..., callToActions[] { diff --git a/studio/schemas/documents/pageBuilder.ts b/studio/schemas/documents/pageBuilder.ts index ee416ba64..e07075bb0 100644 --- a/studio/schemas/documents/pageBuilder.ts +++ b/studio/schemas/documents/pageBuilder.ts @@ -10,6 +10,7 @@ import { employees } from "studio/schemas/objects/sections/employees"; import grid from "studio/schemas/objects/sections/grid"; import hero from "studio/schemas/objects/sections/hero"; import imageSection from "studio/schemas/objects/sections/image"; +import imageSplitSection from "studio/schemas/objects/sections/imagesplit"; import logoSalad from "studio/schemas/objects/sections/logoSalad"; import testimonals from "studio/schemas/objects/sections/testimonials"; import seo from "studio/schemas/objects/seo"; @@ -47,6 +48,7 @@ const pageBuilder = defineType({ callout, callToAction, testimonals, + imageSplitSection, imageSection, grid, employees, diff --git a/studio/schemas/objects/sections/imagesplit.ts b/studio/schemas/objects/sections/imagesplit.ts new file mode 100644 index 000000000..26361b42a --- /dev/null +++ b/studio/schemas/objects/sections/imagesplit.ts @@ -0,0 +1,52 @@ +import { defineField } from "sanity"; + +import { imageExtended } from "studio/schemas/fields/media"; +import { titleID } from "studio/schemas/fields/text"; +import { link } from "studio/schemas/objects/link"; +import { firstTranslation } from "studio/utils/i18n"; + +export const imageID = "imageSplitSection"; + +export const imageSplitSection = defineField({ + name: imageID, + title: "Image Split", + type: "object", + fields: [ + { + name: titleID.basic, + type: "internationalizedArrayString", + title: "Title", + description: + "Enter the primary title that will be displayed at the top of the employees section.", + }, + + { + name: "description", + type: "internationalizedArrayString", + title: "Main content", + description: + "Enter the main content that will be displayed below the title.", + }, + + imageExtended, + { + name: "actions", + title: "Actions (links)", + type: "array", + of: [link], + }, + ], + preview: { + select: { + title: "basicTitle", + }, + prepare(selection) { + const { title } = selection; + return { + title: firstTranslation(title) ?? undefined, + }; + }, + }, +}); + +export default imageSplitSection;