diff --git a/sanityv3/icons/customIcons.tsx b/sanityv3/icons/customIcons.tsx index 77a0712b6..c4800efd2 100644 --- a/sanityv3/icons/customIcons.tsx +++ b/sanityv3/icons/customIcons.tsx @@ -88,6 +88,71 @@ export const LeftAlignedImage = (): JSX.Element => ( ) +export const ContentCenterImage = (): JSX.Element => ( + + + + +) +export const ContentLeftImage = (): JSX.Element => ( + + + + +) + +export const ContentRightImage = (): JSX.Element => ( + + + + +) + export const RightAlignedImage = (): JSX.Element => ( { return route(name, title) @@ -174,6 +175,7 @@ const RemainingSchemas = [ keyNumbers, keyNumberItem, background, + imageBackground, ] // Then we give our schema to the builder and provide the result to Sanity diff --git a/sanityv3/schemas/objects/background/background.tsx b/sanityv3/schemas/objects/background/background.tsx index 0becb213d..fc80126d8 100644 --- a/sanityv3/schemas/objects/background/background.tsx +++ b/sanityv3/schemas/objects/background/background.tsx @@ -1,32 +1,32 @@ import { defineType, defineField } from 'sanity' -export type ColorType = { - title: string - value: string -} - export default defineType({ - name: 'imageBackground', - title: 'Image background', type: 'object', + name: 'backgroundOption', + fields: [ defineField({ - title: 'Background Image', - name: 'image', - type: 'imageWithAlt', - description: 'Alt text is always ignored even if provided, considering background image as decorative.', - }), - defineField({ - title: 'Animation', - name: 'useAnimation', + title: 'Use special background', + name: 'useSpecialBackground', type: 'boolean', - description: 'Animates content over the background image.', }), defineField({ - title: 'Switch to dark theme.', - description: 'Switch to dark theme if the background image is dark.', - name: 'useDarkTheme', - type: 'boolean', + type: 'array', + name: 'background', + description: 'Select what type of background you want to apply', + title: 'Type of background', + of: [{ type: 'imageBackground', title: 'Image Background' }].filter((e) => e), + options: { sortable: false }, + validation: (Rule) => [ + (Rule: Rule) => Rule.required().min(1).max(1), + Rule.required().custom((background, context) => { + if (context.parent.useSpecialBackground) { + return background[0]._type === 'imageBackground' && background[0]?.image?.asset ? true : 'Image required' + } + return true + }), + ], + hidden: ({ parent }: any) => !parent?.useSpecialBackground, }), ], }) diff --git a/sanityv3/schemas/objects/background/imageBackground.tsx b/sanityv3/schemas/objects/background/imageBackground.tsx new file mode 100644 index 000000000..7468ac0d7 --- /dev/null +++ b/sanityv3/schemas/objects/background/imageBackground.tsx @@ -0,0 +1,76 @@ +import { defineType, defineField } from 'sanity' +import { RadioIconSelector } from '../../components' +import { ContentRightImage, ContentLeftImage, ContentCenterImage } from '../../../icons' + +export type ColorType = { + title: string + value: string +} + +const contentAlignmentOptions = [ + { value: 'left', icon: ContentLeftImage }, + { value: 'center', icon: ContentCenterImage }, + { value: 'right', icon: ContentRightImage }, +] + +export default defineType({ + name: 'imageBackground', + title: 'Image background', + type: 'object', + fields: [ + defineField({ + title: 'Background Image', + name: 'image', + type: 'imageWithAlt', + description: 'Alt text is always ignored even if provided, considering background image as decorative.', + }), + defineField({ + title: 'Animation', + name: 'useAnimation', + type: 'boolean', + description: 'Animates content over the background image.', + }), + defineField({ + name: 'contentAlignment', + title: 'Content Alignment', + description: 'Select the content alignment on larger screens.', + type: 'string', + components: { + input: function ({ onChange, value }: { onChange: any; value: string }) { + return ( + + ) + }, + }, + }), + ], + + preview: { + select: { + image: 'image', + useAnimation: 'useAnimation', + contentAlignment: 'contentAlignment', + }, + prepare({ + image, + useAnimation, + contentAlignment, + }: { + image: Reference + useAnimation: boolean + contentAlignment: string + }) { + return { + title: `Image background`, + subtitle: `${contentAlignment.toUpperCase() + ' aligned'} ${useAnimation ? ' | Animated ' : ''} content`, + media: image, + } + }, + }, +}) diff --git a/sanityv3/schemas/objects/textBlock.tsx b/sanityv3/schemas/objects/textBlock.tsx index a5c906a82..dabe5d60b 100644 --- a/sanityv3/schemas/objects/textBlock.tsx +++ b/sanityv3/schemas/objects/textBlock.tsx @@ -62,6 +62,7 @@ export default { name: 'textBlock', title: 'Text block', type: 'object', + fieldsets: [ { title: 'Thumbnail Image', @@ -207,6 +208,7 @@ export default { return !(parent.action && parent?.action.length === 1) }, }, + { title: 'Background', description: @@ -216,10 +218,11 @@ export default { fieldset: 'design', }, { - title: 'Background Image', - description: 'Pick a background image instead of color.', - name: 'imageBackground', - type: 'imageBackground', + title: 'More background options', + description: + 'Pick a colour for the background. Default is white. If background image is not provided this color is used', + name: 'backgroundOption', + type: 'backgroundOption', fieldset: 'design', }, ].filter((e) => e), diff --git a/web/components/src/Backgrounds/BackgroundContainer.tsx b/web/components/src/Backgrounds/BackgroundContainer.tsx index 46edec8a3..8f43da122 100644 --- a/web/components/src/Backgrounds/BackgroundContainer.tsx +++ b/web/components/src/Backgrounds/BackgroundContainer.tsx @@ -1,12 +1,12 @@ import { forwardRef, HTMLAttributes } from 'react' -import type { BackgroundColours, ImageBackground } from '../../../types/types' +import type { BackgroundColours, ImageBackground, BackgroundOption } from '../../../types/types' import { ColouredContainer } from './ColouredContainer' import { ImageBackgroundContainer } from './ImageBackgroundContainer' export type BackgroundContainerProps = { background?: { backgroundColor?: BackgroundColours - imageBackground?: ImageBackground + backgroundOption?: BackgroundOption } } & HTMLAttributes @@ -15,11 +15,11 @@ export const BackgroundContainer = forwardRef { - return background?.imageBackground ? ( + return background?.backgroundOption?.useSpecialBackground ? ( diff --git a/web/components/src/Backgrounds/ColouredContainer.tsx b/web/components/src/Backgrounds/ColouredContainer.tsx index bfbd255a7..898ef1329 100644 --- a/web/components/src/Backgrounds/ColouredContainer.tsx +++ b/web/components/src/Backgrounds/ColouredContainer.tsx @@ -12,6 +12,7 @@ type ColourContainerProps = { } & HTMLAttributes const ColourContainer = styled.div` + container: size; background-color: var(--background-color); color: var(--color-on-background); ${({ isInverted }) => (isInverted ? inverted : normal)} diff --git a/web/components/src/Backgrounds/ImageBackgroundContainer.tsx b/web/components/src/Backgrounds/ImageBackgroundContainer.tsx index 42133bbe8..f50d28517 100644 --- a/web/components/src/Backgrounds/ImageBackgroundContainer.tsx +++ b/web/components/src/Backgrounds/ImageBackgroundContainer.tsx @@ -3,89 +3,134 @@ import { forwardRef, HTMLAttributes, CSSProperties } from 'react' import { useSanityLoader } from '../../../lib/hooks/useSanityLoader' import { ImageBackground } from '../../../types/types' import { normal, inverted } from '../../../styles/themes' +import css from 'styled-jsx/css' type ImageBackgroundContainerProps = { - imageBackground: ImageBackground + background?: BackgroundOptions } & HTMLAttributes + type ImageContainerProps = { imageUrl?: string isInverted: boolean } & HTMLAttributes +type ContentProps = { + useAnimation?: boolean + contentAlignment?: 'left' | 'center' | 'right' +} + const ImageContainer = styled.div` + container: inline-size; position: relative; min-height: 100vh; background-attachment: fixed; background-position: center; background-repeat: no-repeat; background-size: cover; - background-image: ${({ imageUrl }) => (imageUrl ? `url(${imageUrl})` : '')}; + background-image: linear-gradient(rgba(0, 0, 0, 0.4), rgba(0, 0, 0, 0.4)), + ${({ imageUrl }) => (imageUrl ? `url(${imageUrl})` : '')}; ${({ isInverted }) => (isInverted ? inverted : normal)} ` const DEFAULT_MAX_WIDTH = 1920 -const AnimationWrapper = styled.div` +const AnimationWrapper = styled.div` /* Create View Timeline */ - background-image: linear-gradient(to right, rgba(0, 0, 0, 0), var(--color-inverse) 70%); - padding-top: 50vh; - padding-bottom: 50vh; - section { - @media (min-width: 1200px) { - width: 40vw; - margin-right: 0; - padding: var(--space-3xLarge) var(--space-3xLarge); - } - } - display: flex; - view-timeline-name: --revealing-image; - view-timeline-axis: block; + ${(props) => + props.contentAlignment == 'right' && + css` + @media (min-width: 1200px) { + width: 40vw; + } + > section, + > div { + @media (min-width: 1200px) { + margin-right: 0; + width: 100% !important; + padding: var(--space-3xLarge) var(--space-3xLarge); + } + } + `} - /* Attach animation, linked to the View Timeline */ - animation: linear fadeIn both; - animation-timeline: --revealing-image; + ${(props) => + props.useAnimation && + css` + padding-top: 50vh; + padding-bottom: 50vh; - /* Tweak range when effect should run*/ - animation-range: cover 0% cover 100%; + view-timeline-name: --revealing-image; + view-timeline-axis: block; - @keyframes fadeIn { - 0% { - opacity: 0; - } - 20% { - opacity: 1; - } - 80% { - opacity: 1; - } - 100% { - opacity: 0; - } - } + /* Attach animation, linked to the View Timeline */ + animation: linear fadeIn both; + animation-timeline: --revealing-image; + + /* Tweak range when effect should run*/ + animation-range: cover 30% cover 100%; + + @keyframes fadeIn { + 0% { + opacity: 0; + } + 20% { + opacity: 1; + } + 80% { + opacity: 1; + } + 100% { + opacity: 0; + } + } + `} + + + + ${(props) => + props.contentAlignment == 'left' && + css` + @media (min-width: 1200px) { + width: 40vw; + } + > section, + > div { + @media (min-width: 1200px) { + width: 100%; + margin-left: 0; + padding: var(--space-3xLarge) var(--space-3xLarge); + } + } + `} + + display: flex; ` export const ImageBackgroundContainer = forwardRef( function ImageBackgroundContainer({ imageBackground, style, children, className, ...rest }, ref) { const props = useSanityLoader(imageBackground.image, DEFAULT_MAX_WIDTH, undefined) const src = props?.src - const ChildrenWrapper = imageBackground?.useAnimation - ? ({ children }: { children: React.ReactNode }) => {children} - : ({ children }: { children: React.ReactNode }) => {children} + + console.log(imageBackground.contentAlignment) return ( - {children} + + {children} + ) }, diff --git a/web/lib/queries/common/background.ts b/web/lib/queries/common/background.ts index 4c3bfe02c..ce69fca39 100644 --- a/web/lib/queries/common/background.ts +++ b/web/lib/queries/common/background.ts @@ -1,7 +1,10 @@ const background = /* groq */ ` "background": { "backgroundColor": coalesce(background.title, 'White'), - imageBackground + "backgroundOption": { + "useSpecialBackground": backgroundOption.useSpecialBackground, + "background": backgroundOption.background[0] + } }, ` export default background diff --git a/web/types/types.ts b/web/types/types.ts index ffd39276e..b1116c5e3 100644 --- a/web/types/types.ts +++ b/web/types/types.ts @@ -255,7 +255,12 @@ export type LandingPageSchema = { export type ImageBackground = { image?: ImageWithAlt useAnimation?: boolean - useDarkTheme?: boolean + contentAlignment: 'left' | 'right' | 'center' +} + +export type BackgroundOption = { + background: ImageBackground + useSpecialBackground: boolean } export type BackgroundColours = @@ -277,7 +282,7 @@ export type DesignOptions = { imagePosition?: TeaserImagePosition } imageSize?: TeaserImageSize - imageBackground?: ImageBackground + backgroundOption?: BackgroundOption } export type TextBlockData = {