diff --git a/sanityv3/schemas/documents/page.ts b/sanityv3/schemas/documents/page.ts index 8bb4de1c7..32e001190 100644 --- a/sanityv3/schemas/documents/page.ts +++ b/sanityv3/schemas/documents/page.ts @@ -42,6 +42,11 @@ export default { description: 'You can override the hero image as the SoMe image by uploading another image here.', fieldset: 'metadata', }, + { + name: 'stickyMenu', + title: 'Sticky Menu', + type: 'stickyMenu', + }, ...sharedHeroFields, { title: 'Is Campain', diff --git a/sanityv3/schemas/index.ts b/sanityv3/schemas/index.ts index c1d8eb62f..1645a9ce2 100644 --- a/sanityv3/schemas/index.ts +++ b/sanityv3/schemas/index.ts @@ -82,6 +82,7 @@ import transcript from './objects/transcript' import anchorLinkList from './objects/anchorLinkList/anchorLinkList' import anchorLinkReference from './objects/anchorLinkList/anchorLinkReference' import imageForText from './objects/imageForText' +import stickyMenu from './objects/stickyMenu' const { pageNotFound, @@ -210,6 +211,7 @@ const RemainingSchemas = [ anchorLinkList, anchorLinkReference, imageForText, + stickyMenu, ] // Then we give our schema to the builder and provide the result to Sanity diff --git a/sanityv3/schemas/objects/stickyMenu.tsx b/sanityv3/schemas/objects/stickyMenu.tsx new file mode 100644 index 000000000..d47b0a738 --- /dev/null +++ b/sanityv3/schemas/objects/stickyMenu.tsx @@ -0,0 +1,47 @@ +import { Rule } from 'sanity' +import { defaultColors } from '../defaultColors' +import { description } from './iframe/sharedIframeFields' + +const chosenColors = ['White', 'Moss Green Light'] +const backgroundColors = defaultColors.filter((color) => chosenColors.includes(color.title)) + +export default { + name: 'stickyMenu', + title: 'Sticky Menu', + type: 'object', + fields: [ + { + title: 'Title', + name: 'title', + type: 'string', + }, + { + title: 'Links', + name: 'links', + type: 'array', + of: [ + { + type: 'anchorLinkReference', + title: 'Anchor reference', + }, + { type: 'downloadableFile', title: 'Downloadable file' }, + ], + validation: (Rule: Rule) => + Rule.unique() + .max(2) + .custom((value: any) => { + if (value.length == 2 && value[0]._type == value[1]._type) return 'Cannot have two links of same type' + return true + }), + }, + { + title: 'Color', + description: 'Default is white.', + name: 'backgroundColor', + type: 'colorlist', + options: { + colors: backgroundColors, + }, + }, + ], +} diff --git a/web/core/Link/LogoLink.tsx b/web/core/Link/LogoLink.tsx index c3f9d3b8e..ceff10c11 100644 --- a/web/core/Link/LogoLink.tsx +++ b/web/core/Link/LogoLink.tsx @@ -1,18 +1,22 @@ import { AnchorHTMLAttributes } from 'react' import { LogoSecondary } from '@components' import NextLink from 'next/link' +import { twMerge } from 'tailwind-merge' import { useIntl } from 'react-intl' export type LogoLinkProps = AnchorHTMLAttributes -export const LogoLink = ({ ...rest }: LogoLinkProps) => { +export const LogoLink = ({ className, ...rest }: LogoLinkProps) => { const intl = useIntl() return ( diff --git a/web/core/Link/StickyMenuLink.tsx b/web/core/Link/StickyMenuLink.tsx new file mode 100644 index 000000000..202f2cf20 --- /dev/null +++ b/web/core/Link/StickyMenuLink.tsx @@ -0,0 +1,57 @@ +import { forwardRef } from 'react' +import { twMerge } from 'tailwind-merge' +import { BaseLink, BaseLinkProps } from './BaseLink' +import { TransformableIcon } from '../../icons/TransformableIcon' +import { arrow_down, library_pdf } from '@equinor/eds-icons' + +export type StickMenuLinkProps = { + isDownloadable?: boolean +} & BaseLinkProps + +/** Sticky menu link style */ +export const StickyMenuLink = forwardRef(function StickyMenuLink( + { children, type = 'internalUrl', className = '', href = '', isDownloadable = false, ...rest }, + ref, +) { + const classNames = twMerge( + ` + group + inline-flex + align-baseline + w-fit + text-slate-80 + leading-0 + `, + className, + ) + const contentClassNames = ` + relative + w-fit + hover:underline + no-underline + ` + + return ( + + {isDownloadable && } + {children} + {isDownloadable && ( + + )} + {!isDownloadable && ( + + )} + + ) +}) +export default StickyMenuLink diff --git a/web/core/Link/index.ts b/web/core/Link/index.ts index 070e9784a..1ac915013 100644 --- a/web/core/Link/index.ts +++ b/web/core/Link/index.ts @@ -2,4 +2,5 @@ export { default as Link, type LinkProps } from './Link' export { default as ResourceLink, type ResourceLinkProps } from './ResourceLink' export { default as ButtonLink, type ButtonLinkProps } from './ButtonLink' export { default as BaseLink, type BaseLinkProps } from './BaseLink' +export { default as StickyMenuLink, type StickMenuLinkProps } from './StickyMenuLink' export { default as LogoLink, type LogoLinkProps } from './LogoLink' diff --git a/web/lib/queries/common/anchorLinkReferenceFields.ts b/web/lib/queries/common/anchorLinkReferenceFields.ts new file mode 100644 index 000000000..3f5898f21 --- /dev/null +++ b/web/lib/queries/common/anchorLinkReferenceFields.ts @@ -0,0 +1,7 @@ +export const anchorLinkReferenceFields = /* groq */ ` + _type == "anchorLinkReference" =>{ + "type": _type, + "id": _key, + title, + anchorReference, + }` diff --git a/web/lib/queries/common/pageContentFields.ts b/web/lib/queries/common/pageContentFields.ts index c0c345d1f..45ab0f957 100644 --- a/web/lib/queries/common/pageContentFields.ts +++ b/web/lib/queries/common/pageContentFields.ts @@ -14,6 +14,7 @@ import { keyNumbersFields } from './keyNumbersFields' import { noDrafts, sameLang } from './langAndDrafts' import promoteMagazine from './promotions/promoteMagazine' import { lastUpdatedTimeQuery, publishDateTimeQuery } from './publishDateTime' +import { anchorLinkReferenceFields } from './anchorLinkReferenceFields' const pageContentFields = /* groq */ ` _type == "keyNumbers" =>{ @@ -593,10 +594,7 @@ _type == "keyNumbers" =>{ title, columns, "anchorList":anchorList[]{ - "type": _type, - "id": _key, - title, - anchorReference, + ${anchorLinkReferenceFields} } }, _type == "imageForText" => { diff --git a/web/lib/queries/common/stickyMenu.ts b/web/lib/queries/common/stickyMenu.ts new file mode 100644 index 000000000..31af1e380 --- /dev/null +++ b/web/lib/queries/common/stickyMenu.ts @@ -0,0 +1,13 @@ +import downloadableFileFields from './actions/downloadableFileFields' +import { anchorLinkReferenceFields } from './anchorLinkReferenceFields' + +export const stickyMenu = /* groq */ `"stickyMenu": +content->stickyMenu{ + title, +"type": _type, +"links": links[]{ + ${downloadableFileFields}, + ${anchorLinkReferenceFields} +} +} +` diff --git a/web/lib/queries/routes.ts b/web/lib/queries/routes.ts index a91c0e1d1..f3bdc4ff1 100644 --- a/web/lib/queries/routes.ts +++ b/web/lib/queries/routes.ts @@ -4,6 +4,7 @@ import { eventContentFields } from './common/eventContentFields' import { heroFields } from './common/heroFields' import { seoAndSomeFields } from './common/seoAndSomeFields' import { breadcrumbsQuery } from './common/breadcrumbs' +import { stickyMenu } from './common/stickyMenu' const allSlugsQuery = /* groq */ ` "currentSlug": { @@ -22,6 +23,7 @@ export const routeQuery = /* groq */ ` ${allSlugsQuery}, "title": content->title, "seoAndSome": content->${seoAndSomeFields}, + ${stickyMenu}, "hero": content->${heroFields}, "template": content->_type, content->_type == "page" => { diff --git a/web/pageComponents/shared/Header.tsx b/web/pageComponents/shared/Header.tsx index 516cc4d07..6abf74869 100644 --- a/web/pageComponents/shared/Header.tsx +++ b/web/pageComponents/shared/Header.tsx @@ -1,11 +1,10 @@ /* eslint-disable jsx-a11y/anchor-is-valid */ -import styled, { createGlobalStyle } from 'styled-components' -import { CSSProperties } from 'react' +import { createGlobalStyle } from 'styled-components' import { useRouter } from 'next/router' import { default as NextLink } from 'next/link' import { Topbar, BackgroundContainer } from '@components' import { AllSlugsType, LocalizationSwitch } from './LocalizationSwitch' -import type { MenuData, SimpleMenuData } from '../../types/index' +import type { MenuData, SimpleMenuData, StickyMenuData } from '../../types/index' import { Flags } from '../../common/helpers/datasetHelpers' import { languages, defaultLanguage } from '../../languages' import { FormattedMessage, useIntl } from 'react-intl' @@ -15,7 +14,7 @@ import Head from 'next/head' import getConfig from 'next/config' import { getAllSitesLink } from '../../common/helpers/getAllSitesLink' import { Icon } from '@equinor/eds-core-react' -import { ButtonLink, LogoLink } from '@core/Link' +import { ButtonLink, LogoLink, StickyMenuLink } from '@core/Link' import SiteMenu from '@sections/SiteMenu/SiteMenu' const TopbarOffset = createGlobalStyle` @@ -23,51 +22,10 @@ const TopbarOffset = createGlobalStyle` padding-top: var(--topbar-height); } ` - -const HeaderRelative = styled.header` - position: relative; -` - -const TopbarContainer = styled(Topbar.InnerContainer)` - display: grid; - grid-template-areas: 'logo menu'; - grid-template-rows: 1fr; - align-items: center; - grid-column-gap: var(--space-large); - column-gap: var(--space-large); -` - -/* This div is needed because of the grid layout to wrap focus lock panes. - We might look into to improve this at some point. */ -const ControlChild = styled.div`` - -const ControlsContainer = styled.div` - grid-area: menu; - justify-self: right; - display: grid; - grid-template-columns: repeat(var(--columns), auto); - grid-column-gap: var(--space-small); - column-gap: var(--space-small); - align-items: center; - - @media (min-width: 600px) { - grid-column-gap: var(--space-medium); - column-gap: var(--space-medium); - } -` - -const StyledAllSites = styled(NextLink)` - cursor: pointer; - font-size: var(--typeScale-1); - text-decoration: none; - &:hover { - text-decoration: underline; - } -` - export type HeaderProps = { menuData?: MenuData | SimpleMenuData slugs: AllSlugsType + stickyMenuData?: StickyMenuData } const HeadTags = ({ slugs }: { slugs: AllSlugsType }) => { @@ -121,13 +79,13 @@ const HeadTags = ({ slugs }: { slugs: AllSlugsType }) => { const AllSites = () => { const allSitesURL = getAllSitesLink('external') return ( - + - + ) } -const Header = ({ slugs, menuData }: HeaderProps) => { +const Header = ({ slugs, menuData, stickyMenuData }: HeaderProps) => { const router = useRouter() const localization = { activeLocale: router.locale || defaultLanguage.locale, @@ -148,23 +106,24 @@ const Header = ({ slugs, menuData }: HeaderProps) => { const intl = useIntl() const searchLabel = intl.formatMessage({ id: 'search', defaultMessage: 'Search' }) + const anchorReference = stickyMenuData?.links.find((it) => it.type == 'anchorLinkReference') + const resourceLink = stickyMenuData?.links.find((it) => it.type == 'downloadableFile') + return ( - +
- + - {hasSearch && ( - +
{ - +
)} {hasMoreThanOneLanguage && ( @@ -183,19 +142,33 @@ const Header = ({ slugs, menuData }: HeaderProps) => { {shouldDisplayAllSites ? ( ) : menuData && Flags.HAS_FANCY_MENU ? ( - +
- +
) : ( - +
- +
)} -
-
+
+ + {stickyMenuData && ( +
+
{stickyMenuData?.title}
+ + {anchorReference?.title} + + {resourceLink && ( + + {' '} + {resourceLink.label} + + )} +
+ )} -
+ ) } diff --git a/web/pages/[[...slug]].tsx b/web/pages/[[...slug]].tsx index fc0eeb276..dccaee6b5 100644 --- a/web/pages/[[...slug]].tsx +++ b/web/pages/[[...slug]].tsx @@ -86,7 +86,7 @@ Page.getLayout = (page: AppProps) => { return ( <> -
+
{page} diff --git a/web/tailwind.config.cjs b/web/tailwind.config.cjs index e8f1f8bbb..2fadd7832 100644 --- a/web/tailwind.config.cjs +++ b/web/tailwind.config.cjs @@ -193,6 +193,7 @@ module.exports = { 100: 'hsla(212, 82%, 11%, 1)', }, }), + boxShadowColor: { 'moss-green-50': '190deg 9% 67%', 'moss-green-50-interact': '190deg 9% 60%', @@ -294,6 +295,7 @@ module.exports = { boxShadow: { card: 'rgba(0, 0, 0, 0.08) 0px 1px 3px,1px -1px 2px 0px rgba(0, 0, 0, 0.07), rgba(0, 0, 0, 0.20) 0px 1px 2px', 'card-interact': 'rgba(0, 0, 0, 0.14) 0px 1px 3px, rgba(0, 0, 0, 0.36) 0px 1px 2px', + 'top-bar': '0 0 15px 10px rgba(41, 62, 64, 0.15)', }, aspectRatio: { '4/5': '0.8', @@ -496,6 +498,9 @@ module.exports = { }, }), gridTemplateColumns: { + 'auto-1': 'repeat(1,auto)', + 'auto-2': 'repeat(2,auto)', + 'auto-3': 'repeat(3,auto)', 'auto-fill-fr': `repeat(auto-fill, minmax(80px,1fr))`, card: `repeat(auto-fill,minmax(min(100%,220px),400px))`, }, diff --git a/web/types/linkTypes.ts b/web/types/linkTypes.ts index 6ea45b141..cf329af8f 100644 --- a/web/types/linkTypes.ts +++ b/web/types/linkTypes.ts @@ -1,3 +1,5 @@ +import { AnchorLinkReference } from '../types' + export type LinkType = | 'internalUrl' | 'externalUrl' @@ -27,3 +29,14 @@ export type RelatedLinksData = { title: string links: LinkData[] } + +export type StickyMenuLinkType = + | AnchorLinkReference + | { + type: 'downloadableFile' + fileName: string + id: string + label: string + href: string + extension: string + } diff --git a/web/types/types.ts b/web/types/types.ts index af07afaea..386d7affb 100644 --- a/web/types/types.ts +++ b/web/types/types.ts @@ -20,6 +20,7 @@ import type { GridData, FullWidthImageData, FigureData, + StickyMenuLinkType, } from './index' export type IntlData = { @@ -461,17 +462,20 @@ export type PodcastTeaserData = { image: ImageWithAlt designOptions: DesignOptions } + +export type AnchorLinkReference = { + id: string + type: 'anchorLinkReference' + title?: string + anchorReference?: string +} + export type AnchorLinkListData = { id: string type: 'anchorLinkList' title?: string columns?: string - anchorList?: { - id: string - type: 'anchorLinkReference' - title?: string - anchorReference?: string - }[] + anchorList?: AnchorLinkReference[] } export type ImageForTextData = { type: 'imageForText' @@ -480,3 +484,9 @@ export type ImageForTextData = { content?: PortableTextBlock[] aspectRatio?: '16:9' | 'fullWidth' } + +export type StickyMenuData = { + type: 'stickyMenu' + title: string + links: StickyMenuLinkType[] +}