diff --git a/ui/src/react-admin/modules/content-page/components/ContentBlockRenderer/ContentBlockRenderer.const.tsx b/ui/src/react-admin/modules/content-page/components/ContentBlockRenderer/ContentBlockRenderer.const.tsx index 53a9fb6a..d62f1ea1 100644 --- a/ui/src/react-admin/modules/content-page/components/ContentBlockRenderer/ContentBlockRenderer.const.tsx +++ b/ui/src/react-admin/modules/content-page/components/ContentBlockRenderer/ContentBlockRenderer.const.tsx @@ -10,6 +10,7 @@ import { BlockEventbrite } from '~content-blocks/BlockEventbrite'; import { BlockHeading } from '~content-blocks/BlockHeading'; import { BlockHetArchiefHeaderSearch } from '~content-blocks/BlockHetArchiefHeaderSearch/BlockHetArchiefHeaderSearch'; import { BlockHetArchiefImageTextBackground } from '~content-blocks/BlockHetArchiefImageTextBackground'; +import { BlockHomepageBanner } from '~content-blocks/BlockHomepageBanner'; import { BlockIFrame } from '~content-blocks/BlockIFrame'; import { BlockImage } from '~content-blocks/BlockImage'; import { @@ -87,6 +88,7 @@ export function GET_BLOCK_COMPONENT( [ContentBlockType.Breadcrumbs]: BlockBreadcrumbs, [ContentBlockType.HetArchiefImageTextBackground]: BlockHetArchiefImageTextBackground, [ContentBlockType.OverviewWithCarousel]: BlockOverviewWithCarousel, + [ContentBlockType.HomepageBanner]: BlockHomepageBanner, // Avo specific blocks [ContentBlockType.MediaGrid]: loadComponentFromConfig(ContentBlockType.MediaGrid), diff --git a/ui/src/react-admin/modules/content-page/components/blocks/BlockHomepageBanner/BlockHomepageBanner.editorconfig.ts b/ui/src/react-admin/modules/content-page/components/blocks/BlockHomepageBanner/BlockHomepageBanner.editorconfig.ts new file mode 100644 index 00000000..11ee7129 --- /dev/null +++ b/ui/src/react-admin/modules/content-page/components/blocks/BlockHomepageBanner/BlockHomepageBanner.editorconfig.ts @@ -0,0 +1,89 @@ +import { BLOCK_FIELD_DEFAULTS, BLOCK_STATE_DEFAULTS, TEXT_FIELD } from '~content-blocks/defaults'; +import { GET_ALIGN_OPTIONS } from '~modules/content-page/const/get-align-options.ts'; +import { GET_BACKGROUND_COLOR_OPTIONS_ARCHIEF } from '~modules/content-page/const/get-color-options.ts'; +import { + Color, + type ContentBlockConfig, + ContentBlockEditor, + ContentBlockType, + type DefaultContentBlockState, +} from '~modules/content-page/types/content-block.types'; +import { RICH_TEXT_EDITOR_OPTIONS_FULL } from '~shared/consts/rich-text-editor.consts.ts'; +import { tText } from '~shared/helpers/translation-functions'; +import { HET_ARCHIEF } from '~shared/types'; + +export const INITIAL_CONTENT_HOMEPAGE_BANNER_COMPONENTS_STATE = () => ({ + title: '', + content: '', + textAlign: 'left', + backgroundColor: Color.Transparent, + bannerColor: Color.OceanGreen, +}); + +export const INITIAL_CONTENT_HOMEPAGE_BANNER_BLOCK_STATE = (): DefaultContentBlockState => ({ + ...BLOCK_STATE_DEFAULTS(), + fullWidth: true, +}); + +export const CONTENT_HOMEPAGE_BANNER_CONFIG = (position = 0): ContentBlockConfig => ({ + position, + name: tText( + 'modules/content-page/components/blocks/block-homepage-banner/block-homepage-banner___homepage-banner' + ), + type: ContentBlockType.HomepageBanner, + components: { + state: INITIAL_CONTENT_HOMEPAGE_BANNER_COMPONENTS_STATE(), + fields: { + title: TEXT_FIELD( + { + label: tText( + 'modules/content-page/components/blocks/block-homepage-banner/block-homepage-banner___title', + undefined, + [HET_ARCHIEF] + ), + }, + tText( + 'modules/content-page/components/blocks/block-homepage-banner/block-homepage-banner___titel-is-verplicht', + undefined, + [HET_ARCHIEF] + ) + ), + content: TEXT_FIELD({ + editorType: ContentBlockEditor.RICH_TEXT_EDITOR, + editorProps: { + controls: RICH_TEXT_EDITOR_OPTIONS_FULL, + }, + validator: undefined, + }), + textAlign: { + label: tText( + 'modules/content-page/components/blocks/block-homepage-banner/block-homepage-banner___text-alignatie', + undefined, + [HET_ARCHIEF] + ), + editorType: ContentBlockEditor.Select, + editorProps: { + options: GET_ALIGN_OPTIONS(), + }, + }, + bannerColor: { + label: tText( + 'modules/content-page/components/blocks/block-homepage-banner/block-homepage-banner___kleur-banner', + undefined, + [HET_ARCHIEF] + ), + editorType: ContentBlockEditor.ColorSelect, + editorProps: { + options: GET_BACKGROUND_COLOR_OPTIONS_ARCHIEF(), + defaultValue: GET_BACKGROUND_COLOR_OPTIONS_ARCHIEF()[2], + }, + }, + }, + }, + block: { + state: INITIAL_CONTENT_HOMEPAGE_BANNER_BLOCK_STATE(), + fields: { + ...BLOCK_FIELD_DEFAULTS(), + }, + }, +}); diff --git a/ui/src/react-admin/modules/content-page/components/blocks/BlockHomepageBanner/BlockHomepageBanner.scss b/ui/src/react-admin/modules/content-page/components/blocks/BlockHomepageBanner/BlockHomepageBanner.scss new file mode 100644 index 00000000..ab1de890 --- /dev/null +++ b/ui/src/react-admin/modules/content-page/components/blocks/BlockHomepageBanner/BlockHomepageBanner.scss @@ -0,0 +1,226 @@ +@use "sass:math"; +@use "sass:list"; +@use "../../../../shared/styles/settings/variables" as variables; +@use "../../../../shared/styles/settings/colors" as colors; + +// Shared shape sizing for the decorative pattern strips (used by --left and --right). +$pattern-height: 8rem; +$pattern-distance-vertical: variables.$g-spacer-unit * 3; +$pattern-distance-horizontal: variables.$g-spacer-unit * 5; + +$pattern-rect-width: 4rem; +$pattern-circle-diameter: $pattern-height; +$pattern-circle-radius: math.div($pattern-circle-diameter, 2); +$pattern-circle-antialias-edge: $pattern-circle-radius + 0.02rem; +$pattern-cycle-width: $pattern-rect-width * 2 + $pattern-circle-diameter * 3; +// Must stay a multiple of $pattern-cycle-width (see &__pattern) - generous safety margin +// so the strip always reaches this component's own edge on any realistic viewport. +$pattern-repeat-count: 20; +// How far the mobile pattern slots overshoot &__content's edge, so they can still bleed +// past avo2 Container's (smaller, but non-zero) gutter to the true screen edge. +$pattern-mobile-overshoot: 10rem; + +// Renders the repeating tile (3 circles + 2 rects) for one pattern strip. $circle-centers +// is a 3-item list of x-offsets; $rect-ranges is a 2-item list of [start, end] pairs. Both +// sides share this shape/size - only the ORDER (which offsets are passed in) differs. +@mixin pattern-tile($circle-centers, $rect-ranges) { + background-image: + radial-gradient( + circle at list.nth($circle-centers, 1) 50%, + var(--pattern-color, transparent) $pattern-circle-radius, + transparent $pattern-circle-antialias-edge + ), + radial-gradient( + circle at list.nth($circle-centers, 2) 50%, + var(--pattern-color, transparent) $pattern-circle-radius, + transparent $pattern-circle-antialias-edge + ), + radial-gradient( + circle at list.nth($circle-centers, 3) 50%, + var(--pattern-color, transparent) $pattern-circle-radius, + transparent $pattern-circle-antialias-edge + ), + linear-gradient( + to right, + transparent 0 list.nth(list.nth($rect-ranges, 1), 1), + var(--pattern-color, transparent) list.nth(list.nth($rect-ranges, 1), 1) list.nth(list.nth($rect-ranges, 1), 2), + transparent list.nth(list.nth($rect-ranges, 1), 2) list.nth(list.nth($rect-ranges, 2), 1), + var(--pattern-color, transparent) list.nth(list.nth($rect-ranges, 2), 1) list.nth(list.nth($rect-ranges, 2), 2) + ); +} + +.c-block-homepage-banner { + // The pattern strips escape all the way to this element's edges (and beyond) - clip + // them here. clip-path (not overflow-x: hidden) avoids the overflow-x/y coupling rule + // that would otherwise turn overflow-y into "auto" and risk a vertical scrollbar. + clip-path: inset(0); + width: 100% !important; + max-width: none !important; + + &__content { + position: relative; + max-width: 75%; + min-height: $pattern-height; + display: flex; + flex-direction: column; + + @media (max-width: variables.$g-bp4) { + // Mobile pattern slots size themselves as a % of this element, so it needs to + // span the full screen width here, not the desktop 75% cap. + max-width: 100%; + // Reserves normal-flow space for the pattern slots above/below the text. + // &__pattern-slot is position: absolute so its height doesn't count towards + // this element's own height - without this padding, .c-block-homepage-banner's + // clip-path would end right at the text and clip the slots away entirely. + padding: calc($pattern-height + $pattern-distance-vertical) 0; + } + + &--left { + margin-right: auto; + + .c-block-homepage-banner__pattern--right { + left: calc(100% + $pattern-distance-horizontal + $pattern-distance-horizontal); + } + } + + &--center { + margin: 0 auto; + } + + &--right { + margin-left: auto; + + .c-block-homepage-banner__pattern--left{ + right: calc(100% + $pattern-distance-horizontal + $pattern-distance-horizontal); + } + } + + &-title { + padding-bottom: variables.$g-spacer-unit * 4; + color: colors.$color-gray-1000; + + /* H4 */ + font-family: "SofiaPro" !important; + font-size: 3.2rem !important; + font-style: normal !important; + font-weight: 800 !important; + line-height: 4rem !important; + width: 100%; + white-space: wrap; + } + + &-text { + color: colors.$neutral; + + /* H4 */ + font-family: "SofiaPro" !important; + font-size: 1.8rem !important; + font-style: normal !important; + font-weight: 500 !important; + line-height: 2.8rem !important; + } + } + + // Anchored off the text box's own edge (not Container's), so the $pattern-distance-horizontal gap holds + // regardless of alignment or actual (up to 75%) text width. + // + // width MUST be a multiple of $pattern-cycle-width: Chrome has a rendering bug where a + // repeating multi-layer gradient tears in the middle of the element when its width + // isn't an exact multiple of background-size (100vw reproduces this reliably). + // + // --left/--right are two DIFFERENT tiles (not one tile phase-shifted via + // background-position - that proved unreliable), each built so the desired near-text + // shape sits naturally at the tile's own edge, anchored via a plain + // background-position-x: right / left (no offset): + // - left: rect, circle, rect, [circle, circle, rect, circle, ...] + // - right: circle, rect, circle, circle, [rect, circle, circle, rect, circle, ...] + &__pattern { + position: absolute; + top: 0; + bottom: 0; + width: $pattern-cycle-width * $pattern-repeat-count; + background-repeat: repeat-x; + background-position-y: top; + background-size: $pattern-cycle-width $pattern-height; + + &--left { + right: calc(100% + $pattern-distance-horizontal); + background-position-x: right; + @include pattern-tile( + ( + $pattern-circle-radius, + $pattern-circle-diameter + $pattern-circle-radius, + $pattern-circle-diameter * 2 + $pattern-rect-width + $pattern-circle-radius, + ), + ( + ($pattern-circle-diameter * 2, $pattern-circle-diameter * 2 + $pattern-rect-width), + ($pattern-circle-diameter * 3 + $pattern-rect-width, $pattern-cycle-width), + ) + ); + } + + &--right { + left: calc(100% + $pattern-distance-horizontal); + background-position-x: left; + @include pattern-tile( + ( + $pattern-circle-radius, + $pattern-circle-diameter + $pattern-rect-width + $pattern-circle-radius, + $pattern-circle-diameter * 2 + $pattern-rect-width + $pattern-circle-radius, + ), + ( + ($pattern-circle-diameter, $pattern-circle-diameter + $pattern-rect-width), + ($pattern-circle-diameter * 3 + $pattern-rect-width, $pattern-cycle-width), + ) + ); + } + + // Below $g-bp4 the patterns move from beside the text to above/below it (see + // &__pattern-slot). Same tile/anchor as desktop - only the anchor's landmark moves + // from "near text" to the slot's inner (1/3 or 2/3) edge; top/bottom: 0 is already + // set above. + @media (max-width: variables.$g-bp4) { + &--left { + right: 0; + left: auto; + } + + &--right { + left: 0; + right: auto; + } + } + } + + // No-op on desktop (unpositioned - &__pattern's position: absolute reaches past it to + // &__content). Below $g-bp4 it's the positioned, overflow-clipping box that caps + // &__pattern at exactly 1/3 (top) / 2/3 (bottom) of &__content's width. This cap can't + // live on &__pattern itself: capping its width there would make it a non-multiple of + // $pattern-cycle-width again (see the Chrome bug above). + &__pattern-slot { + @media (max-width: variables.$g-bp4) { + position: absolute; + overflow: hidden; + height: $pattern-height; + + // top/bottom: 0 land inside &__content's reserved padding, not against its + // content-box - literal zero offsets, so (unlike the desktop calc(100% + $pattern-distance-horizontal) + // trick) they don't need &__content to have a definite height to resolve. + // + // left/right overshoot by $pattern-mobile-overshoot to reach past avo2 + // Container's gutter to the true screen edge; width compensates so the INNER + // edge (the anchor landmark) stays exactly at 1/3 / 2/3. + &--top { + top: 0; + left: -$pattern-mobile-overshoot; + width: calc(33.333% + #{$pattern-mobile-overshoot}); + } + + &--bottom { + bottom: 0; + right: -$pattern-mobile-overshoot; + width: calc(66.667% + #{$pattern-mobile-overshoot}); + } + } + } +} diff --git a/ui/src/react-admin/modules/content-page/components/blocks/BlockHomepageBanner/BlockHomepageBanner.tsx b/ui/src/react-admin/modules/content-page/components/blocks/BlockHomepageBanner/BlockHomepageBanner.tsx new file mode 100644 index 00000000..4bdc4d8e --- /dev/null +++ b/ui/src/react-admin/modules/content-page/components/blocks/BlockHomepageBanner/BlockHomepageBanner.tsx @@ -0,0 +1,75 @@ +import React, { type CSSProperties, type FunctionComponent, type ReactElement } from 'react'; +import type { DefaultComponentProps } from '~modules/shared/types/components'; +import './BlockHomepageBanner.scss'; +import { Container } from '@viaa/avo2-components'; +import clsx from 'clsx'; +import { BlockHeading } from '~content-blocks/BlockHeading'; +import type { AlignOption } from '~modules/content-page/types/content-block.types'; +import { ContentPageWidth } from '~modules/content-page/types/content-pages.types.ts'; +import Html from '~shared/components/Html/Html.tsx'; + +export interface BlockHomepageBannerProps extends DefaultComponentProps { + title: string; + content: string; + textAlign: AlignOption; + backgroundColor: string; + bannerColor: string; + pageWidth?: string; +} + +export const BlockHomepageBanner: FunctionComponent = ({ + title, + content, + textAlign, + backgroundColor, + bannerColor, + pageWidth, +}): ReactElement => { + return ( +
+ +
+ {/* No-op on desktop (patterns sit beside the text via CSS); on mobile this + is the clip box that caps the pattern at 1/3 screen width - see .scss. + Placed here (before the title) only for mobile's source order. */} +
+ + + {title} + + +
+ +
+ +
+ ); +}; diff --git a/ui/src/react-admin/modules/content-page/components/blocks/BlockHomepageBanner/index.ts b/ui/src/react-admin/modules/content-page/components/blocks/BlockHomepageBanner/index.ts new file mode 100644 index 00000000..d6385e9d --- /dev/null +++ b/ui/src/react-admin/modules/content-page/components/blocks/BlockHomepageBanner/index.ts @@ -0,0 +1,2 @@ +export * from './BlockHomepageBanner.editorconfig.ts'; +export { BlockHomepageBanner } from './BlockHomepageBanner.tsx'; diff --git a/ui/src/react-admin/modules/content-page/components/blocks/BlockOverviewWithCarousel/BlockOverviewWithCarousel.editorconfig.ts b/ui/src/react-admin/modules/content-page/components/blocks/BlockOverviewWithCarousel/BlockOverviewWithCarousel.editorconfig.ts index 0a553154..ffa45d56 100644 --- a/ui/src/react-admin/modules/content-page/components/blocks/BlockOverviewWithCarousel/BlockOverviewWithCarousel.editorconfig.ts +++ b/ui/src/react-admin/modules/content-page/components/blocks/BlockOverviewWithCarousel/BlockOverviewWithCarousel.editorconfig.ts @@ -50,34 +50,67 @@ export const INITIAL_CONTENT_OVERVIEW_WITH_CAROUSEL_BLOCK_STATE = (): DefaultCon export const CONTENT_OVERVIEW_WITH_CAROUSEL_CONFIG = (position = 0): ContentBlockConfig => ({ position, - name: tText('Overzicht met carrousel'), + name: tText( + 'modules/content-page/components/blocks/block-overview-with-carousel/block-overview-with-carousel___overzicht-met-carrousel' + ), type: ContentBlockType.OverviewWithCarousel, components: { state: INITIAL_CONTENT_OVERVIEW_WITH_CAROUSEL_COMPONENTS_STATE(), fields: { title: TEXT_FIELD( { - label: tText('title', undefined, [HET_ARCHIEF]), + label: tText( + 'modules/content-page/components/blocks/block-overview-with-carousel/block-overview-with-carousel___title', + undefined, + [HET_ARCHIEF] + ), }, - tText('titel is verplicht', undefined, [HET_ARCHIEF]) + tText( + 'modules/content-page/components/blocks/block-overview-with-carousel/block-overview-with-carousel___titel-is-verplicht', + undefined, + [HET_ARCHIEF] + ) ), titleType: { - label: tText('titletype', undefined, [HET_ARCHIEF]), + label: tText( + 'modules/content-page/components/blocks/block-overview-with-carousel/block-overview-with-carousel___titletype', + undefined, + [HET_ARCHIEF] + ), editorType: ContentBlockEditor.Select, editorProps: { options: GET_FULL_HEADING_TYPE_OPTIONS(), }, validator: (value: string) => - validateRequiredValue(value, tText('titletype is verplicht', undefined, [HET_ARCHIEF])), + validateRequiredValue( + value, + tText( + 'modules/content-page/components/blocks/block-overview-with-carousel/block-overview-with-carousel___titletype-is-verplicht', + undefined, + [HET_ARCHIEF] + ) + ), }, buttonLabel: TEXT_FIELD({ - label: tText('button label', undefined, [HET_ARCHIEF]), + label: tText( + 'modules/content-page/components/blocks/block-overview-with-carousel/block-overview-with-carousel___button-label', + undefined, + [HET_ARCHIEF] + ), }), buttonAltTitle: TEXT_FIELD({ - label: tText('button alt title', undefined, [HET_ARCHIEF]), + label: tText( + 'modules/content-page/components/blocks/block-overview-with-carousel/block-overview-with-carousel___button-alt-title', + undefined, + [HET_ARCHIEF] + ), }), buttonAction: { - label: tText('button action', undefined, [HET_ARCHIEF]), + label: tText( + 'modules/content-page/components/blocks/block-overview-with-carousel/block-overview-with-carousel___button-action', + undefined, + [HET_ARCHIEF] + ), editorType: ContentBlockEditor.ContentPicker, editorProps: { allowedTypes: [ @@ -88,10 +121,18 @@ export const CONTENT_OVERVIEW_WITH_CAROUSEL_CONFIG = (position = 0): ContentBloc }, }, elements: { - label: tText('Content item', undefined, [HET_ARCHIEF]), + label: tText( + 'modules/content-page/components/blocks/block-overview-with-carousel/block-overview-with-carousel___content-item', + undefined, + [HET_ARCHIEF] + ), fields: { mediaItem: { - label: tText('Item', undefined, [HET_ARCHIEF]), + label: tText( + 'modules/content-page/components/blocks/block-overview-with-carousel/block-overview-with-carousel___item', + undefined, + [HET_ARCHIEF] + ), editorType: ContentBlockEditor.ContentPicker, editorProps: { allowedTypes: [ @@ -102,12 +143,20 @@ export const CONTENT_OVERVIEW_WITH_CAROUSEL_CONFIG = (position = 0): ContentBloc }, }, image: { - label: tText('Item image', undefined, [HET_ARCHIEF]), + label: tText( + 'modules/content-page/components/blocks/block-overview-with-carousel/block-overview-with-carousel___item-image', + undefined, + [HET_ARCHIEF] + ), editorType: ContentBlockEditor.FileUpload, validator: (value: string) => validateRequiredValue( value, - tText('Afbeelding is verplicht', undefined, [HET_ARCHIEF]) + tText( + 'modules/content-page/components/blocks/block-overview-with-carousel/block-overview-with-carousel___afbeelding-is-verplicht', + undefined, + [HET_ARCHIEF] + ) ), editorProps: { assetType: 'CONTENT_BLOCK_IMAGE', @@ -116,38 +165,74 @@ export const CONTENT_OVERVIEW_WITH_CAROUSEL_CONFIG = (position = 0): ContentBloc } as FileUploadProps, }, imageAlt: TEXT_FIELD({ - label: tText('alt tekst voor de afbeelding', undefined, [HET_ARCHIEF]), + label: tText( + 'modules/content-page/components/blocks/block-overview-with-carousel/block-overview-with-carousel___alt-tekst-voor-de-afbeelding', + undefined, + [HET_ARCHIEF] + ), validator: undefined, }), title: TEXT_FIELD( { - label: tText('Item title', undefined, [HET_ARCHIEF]), + label: tText( + 'modules/content-page/components/blocks/block-overview-with-carousel/block-overview-with-carousel___item-title', + undefined, + [HET_ARCHIEF] + ), }, - tText('titel is verplicht', undefined, [HET_ARCHIEF]) + tText( + 'modules/content-page/components/blocks/block-overview-with-carousel/block-overview-with-carousel___titel-is-verplicht', + undefined, + [HET_ARCHIEF] + ) ), backgroundColor: BACKGROUND_COLOR_FIELD( - tText('achtergrondkleur van het item', undefined, [HET_ARCHIEF]), + tText( + 'modules/content-page/components/blocks/block-overview-with-carousel/block-overview-with-carousel___achtergrondkleur-van-het-item', + undefined, + [HET_ARCHIEF] + ), GET_BACKGROUND_COLOR_OPTIONS_ARCHIEF()[1] ), textColor: FOREGROUND_COLOR_FIELD( - tText('tekst kleur', undefined, [HET_ARCHIEF]), + tText( + 'modules/content-page/components/blocks/block-overview-with-carousel/block-overview-with-carousel___tekst-kleur', + undefined, + [HET_ARCHIEF] + ), GET_FOREGROUND_COLOR_OPTIONS_ARCHIEF()[3] ), itemDisplay: { - label: tText('Weergave', undefined, [HET_ARCHIEF]), + label: tText( + 'modules/content-page/components/blocks/block-overview-with-carousel/block-overview-with-carousel___weergave', + undefined, + [HET_ARCHIEF] + ), editorType: ContentBlockEditor.Select, editorProps: { options: [ { - label: tText('Portret', undefined, [HET_ARCHIEF]), + label: tText( + 'modules/content-page/components/blocks/block-overview-with-carousel/block-overview-with-carousel___portret', + undefined, + [HET_ARCHIEF] + ), value: '9:16', }, { - label: tText('Afgerond portret', undefined, [HET_ARCHIEF]), + label: tText( + 'modules/content-page/components/blocks/block-overview-with-carousel/block-overview-with-carousel___afgerond-portret', + undefined, + [HET_ARCHIEF] + ), value: '9:16round', }, { - label: tText('Landscape', undefined, [HET_ARCHIEF]), + label: tText( + 'modules/content-page/components/blocks/block-overview-with-carousel/block-overview-with-carousel___landscape', + undefined, + [HET_ARCHIEF] + ), value: '16:9', }, ], @@ -155,15 +240,27 @@ export const CONTENT_OVERVIEW_WITH_CAROUSEL_CONFIG = (position = 0): ContentBloc validator: (value: string) => validateRequiredValue( value, - tText('Weergave is verplicht', undefined, [HET_ARCHIEF]) + tText( + 'modules/content-page/components/blocks/block-overview-with-carousel/block-overview-with-carousel___weergave-is-verplicht', + undefined, + [HET_ARCHIEF] + ) ), }, }, type: 'fieldGroup', repeat: { defaultState: INITIAL_CONTENT_OVERVIEW_WITH_CAROUSEL_ELEMENT_STATE(), - addButtonLabel: tText('voeg object toe', undefined, [HET_ARCHIEF]), - deleteButtonLabel: tText('verwijder object', undefined, [HET_ARCHIEF]), + addButtonLabel: tText( + 'modules/content-page/components/blocks/block-overview-with-carousel/block-overview-with-carousel___voeg-object-toe', + undefined, + [HET_ARCHIEF] + ), + deleteButtonLabel: tText( + 'modules/content-page/components/blocks/block-overview-with-carousel/block-overview-with-carousel___verwijder-object', + undefined, + [HET_ARCHIEF] + ), }, }, }, diff --git a/ui/src/react-admin/modules/content-page/components/blocks/BlockOverviewWithCarousel/BlockOverviewWithCarousel.tsx b/ui/src/react-admin/modules/content-page/components/blocks/BlockOverviewWithCarousel/BlockOverviewWithCarousel.tsx index 8aafb527..d67432b0 100644 --- a/ui/src/react-admin/modules/content-page/components/blocks/BlockOverviewWithCarousel/BlockOverviewWithCarousel.tsx +++ b/ui/src/react-admin/modules/content-page/components/blocks/BlockOverviewWithCarousel/BlockOverviewWithCarousel.tsx @@ -117,8 +117,16 @@ export const BlockOverviewWithCarousel: FunctionComponent} - title={tText('Vorige slide', undefined, [HET_ARCHIEF])} - ariaLabel={tText('Vorige slide', undefined, [HET_ARCHIEF])} + title={tText( + 'modules/content-page/components/blocks/block-overview-with-carousel/block-overview-with-carousel___vorige-slide', + undefined, + [HET_ARCHIEF] + )} + ariaLabel={tText( + 'modules/content-page/components/blocks/block-overview-with-carousel/block-overview-with-carousel___vorige-slide', + undefined, + [HET_ARCHIEF] + )} onClick={() => controlledSwiper?.slidePrev()} /> )} @@ -126,8 +134,16 @@ export const BlockOverviewWithCarousel: FunctionComponent} - title={tText('Volgende slide', undefined, [HET_ARCHIEF])} - ariaLabel={tText('Volgende slide', undefined, [HET_ARCHIEF])} + title={tText( + 'modules/content-page/components/blocks/block-overview-with-carousel/block-overview-with-carousel___volgende-slide', + undefined, + [HET_ARCHIEF] + )} + ariaLabel={tText( + 'modules/content-page/components/blocks/block-overview-with-carousel/block-overview-with-carousel___volgende-slide', + undefined, + [HET_ARCHIEF] + )} onClick={() => controlledSwiper?.slideNext()} /> )} diff --git a/ui/src/react-admin/modules/content-page/const/content-block-config-map.ts b/ui/src/react-admin/modules/content-page/const/content-block-config-map.ts index 470d2a55..b65c1214 100644 --- a/ui/src/react-admin/modules/content-page/const/content-block-config-map.ts +++ b/ui/src/react-admin/modules/content-page/const/content-block-config-map.ts @@ -11,6 +11,7 @@ import { EVENTBRITE_BLOCK_CONFIG } from '~content-blocks/BlockEventbrite'; import { HEADING_BLOCK_CONFIG } from '~content-blocks/BlockHeading'; import { HET_ARCHIEF_HEADER_SEARCH_BLOCK_CONFIG } from '~content-blocks/BlockHetArchiefHeaderSearch/BlockHetArchiefHeaderSearch.editorconfig'; import { HET_ARCHIEF_IMAGE_TEXT_BACKGROUND_BLOCK_CONFIG } from '~content-blocks/BlockHetArchiefImageTextBackground'; +import { CONTENT_HOMEPAGE_BANNER_CONFIG } from '~content-blocks/BlockHomepageBanner'; import { IFRAME_BLOCK_CONFIG } from '~content-blocks/BlockIFrame'; import { IMAGE_BLOCK_CONFIG } from '~content-blocks/BlockImage'; import { @@ -87,4 +88,5 @@ export const CONTENT_BLOCK_CONFIG_MAP: Record< [ContentBlockType.Breadcrumbs]: CONTENT_BREADCRUMBS_CONFIG, [ContentBlockType.ScrollDownNudge]: CONTENT_SCROLL_DOWN_NUDGE_CONFIG, [ContentBlockType.OverviewWithCarousel]: CONTENT_OVERVIEW_WITH_CAROUSEL_CONFIG, + [ContentBlockType.HomepageBanner]: CONTENT_HOMEPAGE_BANNER_CONFIG, }; diff --git a/ui/src/react-admin/modules/content-page/const/content-block-initial-state-map.ts b/ui/src/react-admin/modules/content-page/const/content-block-initial-state-map.ts index a864713a..2f689b91 100644 --- a/ui/src/react-admin/modules/content-page/const/content-block-initial-state-map.ts +++ b/ui/src/react-admin/modules/content-page/const/content-block-initial-state-map.ts @@ -11,6 +11,7 @@ import { INITIAL_EVENTBRITE_COMPONENTS_STATE } from '~content-blocks/BlockEventb import { INITIAL_HEADING_COMPONENTS_STATE } from '~content-blocks/BlockHeading'; import { INITIAL_HET_ARCHIEF_HEADER_SEARCH_BLOCK_STATE } from '~content-blocks/BlockHetArchiefHeaderSearch/BlockHetArchiefHeaderSearch.editorconfig'; import { INITIAL_HET_ARCHIEF_IMAGE_TEXT_BACKGROUND_BLOCK_STATE } from '~content-blocks/BlockHetArchiefImageTextBackground'; +import { INITIAL_CONTENT_HOMEPAGE_BANNER_BLOCK_STATE } from '~content-blocks/BlockHomepageBanner'; import { INITIAL_IFRAME_COMPONENTS_STATE } from '~content-blocks/BlockIFrame'; import { INITIAL_IMAGE_COMPONENTS_STATE } from '~content-blocks/BlockImage'; import { @@ -90,4 +91,5 @@ export const CONTENT_BLOCK_INITIAL_STATE_MAP: { [ContentBlockType.Breadcrumbs]: INITIAL_BREADCRUMBS_BLOCK_STATE, [ContentBlockType.ScrollDownNudge]: INITIAL_SCROLL_DOWN_NUDGE_BLOCK_STATE, [ContentBlockType.OverviewWithCarousel]: INITIAL_CONTENT_OVERVIEW_WITH_CAROUSEL_BLOCK_STATE, + [ContentBlockType.HomepageBanner]: INITIAL_CONTENT_HOMEPAGE_BANNER_BLOCK_STATE, }; diff --git a/ui/src/react-admin/modules/content-page/const/get-content-block-type-options.ts b/ui/src/react-admin/modules/content-page/const/get-content-block-type-options.ts index 94bcf7c7..e4c81af7 100644 --- a/ui/src/react-admin/modules/content-page/const/get-content-block-type-options.ts +++ b/ui/src/react-admin/modules/content-page/const/get-content-block-type-options.ts @@ -184,6 +184,14 @@ export const GET_CONTENT_BLOCK_TYPE_OPTIONS: () => SelectOption[] = () = ), value: ContentBlockType.OverviewWithCarousel, }, + { + label: tText( + 'modules/content-page/const/get-content-block-type-options___homepage=-banner', + {}, + [HET_ARCHIEF] + ), + value: ContentBlockType.HomepageBanner, + }, ]; // Only show the content blocks that the client enabled through the config object diff --git a/ui/src/react-admin/modules/content-page/types/content-block.types.ts b/ui/src/react-admin/modules/content-page/types/content-block.types.ts index 5135bb25..28ec3b58 100644 --- a/ui/src/react-admin/modules/content-page/types/content-block.types.ts +++ b/ui/src/react-admin/modules/content-page/types/content-block.types.ts @@ -187,6 +187,7 @@ export enum ContentBlockType { AvoImageTextBackground = 'AVO_IMAGE_TEXT_BACKGROUND', // Avo ScrollDownNudge = 'SCROLL_DOWN_NUDGE', OverviewWithCarousel = 'OVERVIEW_WITH_CAROUSEL', + HomepageBanner = 'HOMEPAGE_BANNER', } export enum ContentBlockEditor { diff --git a/ui/src/react-admin/modules/shared/styles/settings/_colors.scss b/ui/src/react-admin/modules/shared/styles/settings/_colors.scss index 1e40099a..f57c8827 100644 --- a/ui/src/react-admin/modules/shared/styles/settings/_colors.scss +++ b/ui/src/react-admin/modules/shared/styles/settings/_colors.scss @@ -74,3 +74,4 @@ $color-klascement: #f7931b; $color-sea-green: #009690; $color-platinum: #f8f8f8; +$neutral: #727272;