From d0d63d163e69b98d8c0c7fe5ea2e5bdc2aaa0cb9 Mon Sep 17 00:00:00 2001 From: Mikael Brevik Date: Fri, 29 Nov 2024 15:43:27 +0100 Subject: [PATCH 1/6] feat: adds image split module --- .../sections/image-split/ImageSplit.tsx | 42 +++++++++++++++++++ .../image-split/ImageSplitPreview.tsx | 36 ++++++++++++++++ .../image-split/image-split.module.css | 23 ++++++++++ src/utils/renderSection.tsx | 33 ++++++++++++++- studio/lib/interfaces/pages.ts | 10 +++++ studio/lib/queries/pages.ts | 7 ++++ studio/schemas/documents/pageBuilder.ts | 2 + studio/schemas/objects/sections/imagesplit.ts | 28 +++++++++++++ 8 files changed, 180 insertions(+), 1 deletion(-) create mode 100644 src/components/sections/image-split/ImageSplit.tsx create mode 100644 src/components/sections/image-split/ImageSplitPreview.tsx create mode 100644 src/components/sections/image-split/image-split.module.css create mode 100644 studio/schemas/objects/sections/imagesplit.ts diff --git a/src/components/sections/image-split/ImageSplit.tsx b/src/components/sections/image-split/ImageSplit.tsx new file mode 100644 index 000000000..32c2f617a --- /dev/null +++ b/src/components/sections/image-split/ImageSplit.tsx @@ -0,0 +1,42 @@ +import { PortableText, PortableTextReactComponents } from "@portabletext/react"; + +import { SanityImage } from "src/components/image/SanityImage"; +import CustomLink from "src/components/link/CustomLink"; +import Text from "src/components/text/Text"; +import { ImageSplitSection } from "studio/lib/interfaces/pages"; + +import styles from "./image-split.module.css"; + +interface ImageSplitProps { + section: ImageSplitSection; +} + +const myPortableTextComponents: Partial = { + block: ({ children }) => {children}, +}; + +const ImageSplitComponent = ({ section }: ImageSplitProps) => { + return ( +
+ {section.basicTitle} + +
+ {section.richText && ( + + )} + {section.link && } +
+ + {section.imageExtended && ( +
+ +
+ )} +
+ ); +}; + +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..d601f45c1 --- /dev/null +++ b/src/components/sections/image-split/image-split.module.css @@ -0,0 +1,23 @@ +.shared { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + +.callout { + composes: shared; + width: -webkit-fill-available; + max-width: 75rem; + gap: 3rem; + + padding: 5rem 2rem; + + @media (min-width: 640px) { + padding: 5rem 3rem; + } + + @media (min-width: 1024px) { + padding: 7.5rem; + } +} 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..c5de7dbe7 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; + richText?: PortableTextBlock[]; + link?: 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..76b488077 100644 --- a/studio/lib/queries/pages.ts +++ b/studio/lib/queries/pages.ts @@ -27,6 +27,13 @@ const SECTIONS_FRAGMENT = groq` ${TRANSLATED_LINK_FRAGMENT} } }, + _type == "imageSplitSection" => { + ..., + link { + ..., + ${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..c5cbef4c2 --- /dev/null +++ b/studio/schemas/objects/sections/imagesplit.ts @@ -0,0 +1,28 @@ +import { defineField } from "sanity"; + +import { imageExtended } from "studio/schemas/fields/media"; +import { richText, title } from "studio/schemas/fields/text"; +import { link } from "studio/schemas/objects/link"; + +export const imageID = "imageSplitSection"; + +export const imageSplitSection = defineField({ + name: imageID, + title: "Image Split", + type: "object", + fields: [title, richText, imageExtended, link], + preview: { + select: { + title: "basicTitle", + }, + prepare(selection) { + const { title } = selection; + return { + title: title, + subtitle: "Image", + }; + }, + }, +}); + +export default imageSplitSection; From 7c20d8cd10b473d8b0f1bb29ee87a91ddcb06076 Mon Sep 17 00:00:00 2001 From: Mikael Brevik Date: Fri, 29 Nov 2024 18:47:13 +0100 Subject: [PATCH 2/6] fix: adds style to split image --- src/components/linkButton/LinkButton.tsx | 6 +-- .../sections/image-split/ImageSplit.tsx | 19 ++++++--- .../image-split/image-split.module.css | 40 +++++++++++-------- 3 files changed, 39 insertions(+), 26 deletions(-) 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 index 32c2f617a..58a558585 100644 --- a/src/components/sections/image-split/ImageSplit.tsx +++ b/src/components/sections/image-split/ImageSplit.tsx @@ -1,7 +1,7 @@ import { PortableText, PortableTextReactComponents } from "@portabletext/react"; import { SanityImage } from "src/components/image/SanityImage"; -import CustomLink from "src/components/link/CustomLink"; +import LinkButton from "src/components/linkButton/LinkButton"; import Text from "src/components/text/Text"; import { ImageSplitSection } from "studio/lib/interfaces/pages"; @@ -12,22 +12,29 @@ interface ImageSplitProps { } const myPortableTextComponents: Partial = { - block: ({ children }) => {children}, + block: ({ children }) => {children}, }; const ImageSplitComponent = ({ section }: ImageSplitProps) => { return ( -
- {section.basicTitle} +
+
+ + {section.basicTitle} + -
{section.richText && ( )} - {section.link && } + + {section.link && ( +
+ +
+ )}
{section.imageExtended && ( diff --git a/src/components/sections/image-split/image-split.module.css b/src/components/sections/image-split/image-split.module.css index d601f45c1..c0d2da57b 100644 --- a/src/components/sections/image-split/image-split.module.css +++ b/src/components/sections/image-split/image-split.module.css @@ -1,23 +1,29 @@ -.shared { - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; -} +.imageSplit { + max-width: var(--max-content-width-small); + + display: grid; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); -.callout { - composes: shared; - width: -webkit-fill-available; - max-width: 75rem; gap: 3rem; + row-gap: 5.25rem; + padding: 1rem; + margin: 3.75rem auto; - padding: 5rem 2rem; + box-sizing: content-box; + align-items: center; +} - @media (min-width: 640px) { - padding: 5rem 3rem; - } +.textContainer { + display: flex; + flex-direction: column; + gap: 0.5rem; +} - @media (min-width: 1024px) { - padding: 7.5rem; - } +.textContainer__link { + margin-top: 1.5rem; +} + +.image { + display: flex; + justify-content: center; } From 77c538a716b1f938fdb24d21b97a81d617399893 Mon Sep 17 00:00:00 2001 From: Mikael Brevik Date: Fri, 29 Nov 2024 19:18:37 +0100 Subject: [PATCH 3/6] feat: support left and right image placement --- src/components/sections/image-split/ImageSplit.tsx | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/components/sections/image-split/ImageSplit.tsx b/src/components/sections/image-split/ImageSplit.tsx index 58a558585..680b96d36 100644 --- a/src/components/sections/image-split/ImageSplit.tsx +++ b/src/components/sections/image-split/ImageSplit.tsx @@ -6,6 +6,7 @@ import Text from "src/components/text/Text"; import { ImageSplitSection } from "studio/lib/interfaces/pages"; import styles from "./image-split.module.css"; +import { ImageAlignment } from "studio/schemas/fields/media"; interface ImageSplitProps { section: ImageSplitSection; @@ -16,8 +17,19 @@ const myPortableTextComponents: Partial = { }; 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} @@ -37,7 +49,7 @@ const ImageSplitComponent = ({ section }: ImageSplitProps) => { )}
- {section.imageExtended && ( + {showImageToRight && (
From 989894740ead78f3d59a5490319f01e9c3c00802 Mon Sep 17 00:00:00 2001 From: Mikael Brevik Date: Fri, 29 Nov 2024 19:21:03 +0100 Subject: [PATCH 4/6] lint --- src/components/sections/image-split/ImageSplit.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/sections/image-split/ImageSplit.tsx b/src/components/sections/image-split/ImageSplit.tsx index 680b96d36..64a7f7bee 100644 --- a/src/components/sections/image-split/ImageSplit.tsx +++ b/src/components/sections/image-split/ImageSplit.tsx @@ -4,9 +4,9 @@ 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"; -import { ImageAlignment } from "studio/schemas/fields/media"; interface ImageSplitProps { section: ImageSplitSection; From 0055bc1941e9515d3c63cc510a06e261a7d8e51a Mon Sep 17 00:00:00 2001 From: Mikael Brevik Date: Mon, 2 Dec 2024 11:02:14 +0100 Subject: [PATCH 5/6] feat: adds option to have several links --- src/components/sections/image-split/ImageSplit.tsx | 11 +++++++++-- .../sections/image-split/image-split.module.css | 3 +++ studio/lib/interfaces/pages.ts | 2 +- studio/lib/queries/pages.ts | 2 +- studio/schemas/objects/sections/imagesplit.ts | 12 +++++++++++- 5 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/components/sections/image-split/ImageSplit.tsx b/src/components/sections/image-split/ImageSplit.tsx index 64a7f7bee..704bc1be2 100644 --- a/src/components/sections/image-split/ImageSplit.tsx +++ b/src/components/sections/image-split/ImageSplit.tsx @@ -42,9 +42,16 @@ const ImageSplitComponent = ({ section }: ImageSplitProps) => { /> )} - {section.link && ( + {section.actions.length > 0 && (
- + {section.actions.map((action, index) => ( + + ))}
)}
diff --git a/src/components/sections/image-split/image-split.module.css b/src/components/sections/image-split/image-split.module.css index c0d2da57b..7537f129e 100644 --- a/src/components/sections/image-split/image-split.module.css +++ b/src/components/sections/image-split/image-split.module.css @@ -21,6 +21,9 @@ .textContainer__link { margin-top: 1.5rem; + display: flex; + flex-wrap: wrap; + gap: 0.5rem; } .image { diff --git a/studio/lib/interfaces/pages.ts b/studio/lib/interfaces/pages.ts index c5de7dbe7..76cce87af 100644 --- a/studio/lib/interfaces/pages.ts +++ b/studio/lib/interfaces/pages.ts @@ -74,7 +74,7 @@ export interface ImageSplitSection { basicTitle: string; imageExtended: ImageExtendedProps; richText?: PortableTextBlock[]; - link?: ILink; + actions: ILink[]; } export interface GridSection { diff --git a/studio/lib/queries/pages.ts b/studio/lib/queries/pages.ts index 76b488077..84f006331 100644 --- a/studio/lib/queries/pages.ts +++ b/studio/lib/queries/pages.ts @@ -29,7 +29,7 @@ const SECTIONS_FRAGMENT = groq` }, _type == "imageSplitSection" => { ..., - link { + actions[] { ..., ${TRANSLATED_LINK_FRAGMENT} } diff --git a/studio/schemas/objects/sections/imagesplit.ts b/studio/schemas/objects/sections/imagesplit.ts index c5cbef4c2..7fdcd2324 100644 --- a/studio/schemas/objects/sections/imagesplit.ts +++ b/studio/schemas/objects/sections/imagesplit.ts @@ -10,7 +10,17 @@ export const imageSplitSection = defineField({ name: imageID, title: "Image Split", type: "object", - fields: [title, richText, imageExtended, link], + fields: [ + title, + richText, + imageExtended, + { + name: "actions", + title: "Actions (links)", + type: "array", + of: [link], + }, + ], preview: { select: { title: "basicTitle", From a4da530a14aada18397937eb8d641730e83aa4ee Mon Sep 17 00:00:00 2001 From: Mikael Brevik Date: Mon, 2 Dec 2024 14:27:55 +0100 Subject: [PATCH 6/6] fix: changes title and description to translateable string --- .../sections/image-split/ImageSplit.tsx | 13 ++-------- studio/lib/interfaces/pages.ts | 2 +- studio/lib/queries/pages.ts | 2 ++ studio/schemas/objects/sections/imagesplit.ts | 24 +++++++++++++++---- 4 files changed, 24 insertions(+), 17 deletions(-) diff --git a/src/components/sections/image-split/ImageSplit.tsx b/src/components/sections/image-split/ImageSplit.tsx index 704bc1be2..c1f038b70 100644 --- a/src/components/sections/image-split/ImageSplit.tsx +++ b/src/components/sections/image-split/ImageSplit.tsx @@ -1,5 +1,3 @@ -import { PortableText, PortableTextReactComponents } from "@portabletext/react"; - import { SanityImage } from "src/components/image/SanityImage"; import LinkButton from "src/components/linkButton/LinkButton"; import Text from "src/components/text/Text"; @@ -12,10 +10,6 @@ interface ImageSplitProps { section: ImageSplitSection; } -const myPortableTextComponents: Partial = { - block: ({ children }) => {children}, -}; - const ImageSplitComponent = ({ section }: ImageSplitProps) => { const hasImage = section.imageExtended; const alignment = section.imageExtended?.imageAlignment; @@ -35,11 +29,8 @@ const ImageSplitComponent = ({ section }: ImageSplitProps) => { {section.basicTitle} - {section.richText && ( - + {section.description && ( + {section.description} )} {section.actions.length > 0 && ( diff --git a/studio/lib/interfaces/pages.ts b/studio/lib/interfaces/pages.ts index 76cce87af..5e65e52a6 100644 --- a/studio/lib/interfaces/pages.ts +++ b/studio/lib/interfaces/pages.ts @@ -73,7 +73,7 @@ export interface ImageSplitSection { _key: string; basicTitle: string; imageExtended: ImageExtendedProps; - richText?: PortableTextBlock[]; + description: string; actions: ILink[]; } diff --git a/studio/lib/queries/pages.ts b/studio/lib/queries/pages.ts index 84f006331..bd3cc2d17 100644 --- a/studio/lib/queries/pages.ts +++ b/studio/lib/queries/pages.ts @@ -29,6 +29,8 @@ const SECTIONS_FRAGMENT = groq` }, _type == "imageSplitSection" => { ..., + "basicTitle": ${translatedFieldFragment("basicTitle")}, + "description": ${translatedFieldFragment("description")}, actions[] { ..., ${TRANSLATED_LINK_FRAGMENT} diff --git a/studio/schemas/objects/sections/imagesplit.ts b/studio/schemas/objects/sections/imagesplit.ts index 7fdcd2324..26361b42a 100644 --- a/studio/schemas/objects/sections/imagesplit.ts +++ b/studio/schemas/objects/sections/imagesplit.ts @@ -1,8 +1,9 @@ import { defineField } from "sanity"; import { imageExtended } from "studio/schemas/fields/media"; -import { richText, title } from "studio/schemas/fields/text"; +import { titleID } from "studio/schemas/fields/text"; import { link } from "studio/schemas/objects/link"; +import { firstTranslation } from "studio/utils/i18n"; export const imageID = "imageSplitSection"; @@ -11,8 +12,22 @@ export const imageSplitSection = defineField({ title: "Image Split", type: "object", fields: [ - title, - richText, + { + 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", @@ -28,8 +43,7 @@ export const imageSplitSection = defineField({ prepare(selection) { const { title } = selection; return { - title: title, - subtitle: "Image", + title: firstTranslation(title) ?? undefined, }; }, },