diff --git a/apps/cms/config/sync/system.menu.meta.yml b/apps/cms/config/sync/system.menu.meta.yml new file mode 100644 index 000000000..ef60f59a2 --- /dev/null +++ b/apps/cms/config/sync/system.menu.meta.yml @@ -0,0 +1,8 @@ +uuid: ed03f628-2d83-4f82-aeb8-6c59d150757d +langcode: en +status: true +dependencies: { } +id: meta +label: Meta +description: 'Meta links.' +locked: false diff --git a/apps/preview/.lagoon.env b/apps/preview/.lagoon.env index 752e3109d..e10314091 100644 --- a/apps/preview/.lagoon.env +++ b/apps/preview/.lagoon.env @@ -1 +1,2 @@ +PROJECT_NAME="example" DRUPAL_URL="https://nginx.${LAGOON_ENVIRONMENT}.${LAGOON_PROJECT}.ch4.amazee.io" diff --git a/apps/website/package.json b/apps/website/package.json index 2954fe07e..8ba4d3ae7 100644 --- a/apps/website/package.json +++ b/apps/website/package.json @@ -8,7 +8,7 @@ "@amazeelabs/gatsby-plugin-operations": "^1.1.3", "@amazeelabs/gatsby-plugin-static-dirs": "^1.0.1", "@amazeelabs/gatsby-source-silverback": "^1.14.0", - "@amazeelabs/publisher": "^2.4.17", + "@amazeelabs/publisher": "^2.4.30", "@amazeelabs/strangler-netlify": "^1.1.9", "@amazeelabs/token-auth-middleware": "^1.1.1", "@custom/cms": "workspace:*", diff --git a/packages/drupal/test_content/content/menu_link_content/4aa4a86f-8e0f-441d-8fcb-ac95b15ed13c.yml b/packages/drupal/test_content/content/menu_link_content/4aa4a86f-8e0f-441d-8fcb-ac95b15ed13c.yml new file mode 100644 index 000000000..430e6e5b4 --- /dev/null +++ b/packages/drupal/test_content/content/menu_link_content/4aa4a86f-8e0f-441d-8fcb-ac95b15ed13c.yml @@ -0,0 +1,68 @@ +_meta: + version: '1.0' + entity_type: menu_link_content + uuid: 4aa4a86f-8e0f-441d-8fcb-ac95b15ed13c + bundle: menu_link_content + default_langcode: en +default: + enabled: + - + value: true + title: + - + value: 'Meta item 2' + menu_name: + - + value: meta + link: + - + uri: 'internal:/' + title: '' + options: { } + external: + - + value: false + rediscover: + - + value: true + weight: + - + value: -49 + expanded: + - + value: false + content_translation_source: + - + value: und + content_translation_outdated: + - + value: false + content_translation_uid: + - + target_id: 1 + content_translation_status: + - + value: true + content_translation_created: + - + value: 1716899458 +translations: + de: + title: + - + value: 'Meta item 2 DE' + content_translation_source: + - + value: en + content_translation_outdated: + - + value: false + content_translation_uid: + - + target_id: 1 + content_translation_status: + - + value: true + content_translation_created: + - + value: 1716900493 diff --git a/packages/drupal/test_content/content/menu_link_content/679de789-7c88-494e-866d-062c2abf8fbb.yml b/packages/drupal/test_content/content/menu_link_content/679de789-7c88-494e-866d-062c2abf8fbb.yml new file mode 100644 index 000000000..5c8065b71 --- /dev/null +++ b/packages/drupal/test_content/content/menu_link_content/679de789-7c88-494e-866d-062c2abf8fbb.yml @@ -0,0 +1,68 @@ +_meta: + version: '1.0' + entity_type: menu_link_content + uuid: 679de789-7c88-494e-866d-062c2abf8fbb + bundle: menu_link_content + default_langcode: en +default: + enabled: + - + value: true + title: + - + value: 'Meta item 1' + menu_name: + - + value: meta + link: + - + uri: 'internal:/' + title: '' + options: { } + external: + - + value: false + rediscover: + - + value: true + weight: + - + value: -50 + expanded: + - + value: false + content_translation_source: + - + value: und + content_translation_outdated: + - + value: false + content_translation_uid: + - + target_id: 1 + content_translation_status: + - + value: true + content_translation_created: + - + value: 1716899070 +translations: + de: + title: + - + value: 'Meta item 1 DE' + content_translation_source: + - + value: en + content_translation_outdated: + - + value: false + content_translation_uid: + - + target_id: 1 + content_translation_status: + - + value: true + content_translation_created: + - + value: 1716900444 diff --git a/packages/drupal/test_content/content/menu_link_content/72904161-a94f-4934-98aa-f4b67e2dc9d5.yml b/packages/drupal/test_content/content/menu_link_content/72904161-a94f-4934-98aa-f4b67e2dc9d5.yml new file mode 100644 index 000000000..34c48f445 --- /dev/null +++ b/packages/drupal/test_content/content/menu_link_content/72904161-a94f-4934-98aa-f4b67e2dc9d5.yml @@ -0,0 +1,73 @@ +_meta: + version: '1.0' + entity_type: menu_link_content + uuid: 72904161-a94f-4934-98aa-f4b67e2dc9d5 + bundle: menu_link_content + default_langcode: en + depends: + 679de789-7c88-494e-866d-062c2abf8fbb: menu_link_content +default: + enabled: + - + value: true + title: + - + value: 'Meta item child - should be ignored' + menu_name: + - + value: meta + link: + - + uri: 'internal:/' + title: '' + options: { } + external: + - + value: false + rediscover: + - + value: true + weight: + - + value: -50 + expanded: + - + value: false + parent: + - + value: 'menu_link_content:679de789-7c88-494e-866d-062c2abf8fbb' + content_translation_source: + - + value: und + content_translation_outdated: + - + value: false + content_translation_uid: + - + target_id: 1 + content_translation_status: + - + value: true + content_translation_created: + - + value: 1716899647 +translations: + de: + title: + - + value: 'Meta item child - should be ignored DE' + content_translation_source: + - + value: en + content_translation_outdated: + - + value: false + content_translation_uid: + - + target_id: 1 + content_translation_status: + - + value: true + content_translation_created: + - + value: 1716900507 diff --git a/packages/drupal/test_content/content/node/a397ca48-8fad-411e-8901-0eba2feb989c.yml b/packages/drupal/test_content/content/node/a397ca48-8fad-411e-8901-0eba2feb989c.yml index dfac09fa5..9c608e7ff 100644 --- a/packages/drupal/test_content/content/node/a397ca48-8fad-411e-8901-0eba2feb989c.yml +++ b/packages/drupal/test_content/content/node/a397ca48-8fad-411e-8901-0eba2feb989c.yml @@ -11,43 +11,31 @@ _meta: 72187a1f-3e48-4b45-a9b7-189c6fd7ee26: media default: revision_uid: - - - target_id: 1 + - target_id: 1 status: - - - value: true + - value: true uid: - - - target_id: 1 + - target_id: 1 title: - - - value: 'Blocks: complete' + - value: 'Blocks: complete' created: - - - value: 1686759493 + - value: 1686759493 promote: - - - value: false + - value: false sticky: - - - value: false + - value: false moderation_state: - - - value: published + - value: published path: - - - alias: /blocks-complete + - alias: /blocks-complete langcode: en pathauto: 0 content_translation_source: - - - value: und + - value: und content_translation_outdated: - - - value: false + - value: false body: - - - value: |- + - value: |- @@ -143,40 +131,29 @@ default: translations: de: status: - - - value: true + - value: true uid: - - - target_id: 1 + - target_id: 1 title: - - - value: 'Blocks: complete DE' + - value: 'Blocks: complete DE' created: - - - value: 1687338353 + - value: 1687338353 promote: - - - value: false + - value: false sticky: - - - value: false + - value: false moderation_state: - - - value: published + - value: published path: - - - alias: /blocks-complete + - alias: /blocks-complete langcode: de pathauto: 0 content_translation_source: - - - value: en + - value: en content_translation_outdated: - - - value: false + - value: false body: - - - value: |- + - value: |- @@ -215,4 +192,4 @@ translations: format: gutenberg - summary: '' + summary: '' \ No newline at end of file diff --git a/packages/schema/src/operations/Frame.gql b/packages/schema/src/operations/Frame.gql index 71e0dca6f..fe939e41b 100644 --- a/packages/schema/src/operations/Frame.gql +++ b/packages/schema/src/operations/Frame.gql @@ -7,6 +7,9 @@ query Frame { } } } + metaNavigation: metaNavigations { + ...Navigation + } mainNavigation: mainNavigations { ...Navigation } diff --git a/packages/schema/src/schema.graphql b/packages/schema/src/schema.graphql index 4c0150a42..4df4d5852 100644 --- a/packages/schema/src/schema.graphql +++ b/packages/schema/src/schema.graphql @@ -118,6 +118,11 @@ interface Navigation { items: [NavigationItem]! } +type MetaNavigation implements Navigation @menu(menu_id: "meta") { + locale: Locale! @resolveEntityLanguage + items: [NavigationItem]! @lang @resolveMenuItems(max_level: 1) +} + type MainNavigation implements Navigation @menu(menu_id: "main") { locale: Locale! @resolveEntityLanguage items: [NavigationItem]! @lang @resolveMenuItems @@ -277,6 +282,18 @@ enum CTAIconPosition { BEFORE } +type BlockImageWithText @type(id: "custom/image-with-text") { + image: MediaImage @resolveEditorBlockMedia + textContent: BlockMarkup @resolveEditorBlockChildren @seek(pos: 0) +} + +type BlockQuote @type(id: "custom/quote") { + quote: Markup @resolveEditorBlockAttribute(key: "quote") + author: String @resolveEditorBlockAttribute(key: "author") + role: String @resolveEditorBlockAttribute(key: "role") + image: MediaImage @resolveEditorBlockMedia +} + type BlockImageWithText @type(id: "custom/image-with-text") { image: MediaImage @resolveEditorBlockMedia imagePosition: ImagePosition! @@ -328,6 +345,10 @@ type Query { previewDrupalPage(id: ID!, rid: ID, locale: String!): DrupalPage @fetchEntity(type: "node", id: "$id", rid: "$rid", language: "$locale") + metaNavigations: [MetaNavigation] + @gatsbyNodes(type: "MetaNavigation") + @menuTranslations(menu_id: "meta") + mainNavigations: [MainNavigation] @gatsbyNodes(type: "MainNavigation") @menuTranslations(menu_id: "main") diff --git a/packages/ui/src/components/Client/DesktopMenu.tsx b/packages/ui/src/components/Client/DesktopMenu.tsx index 756031972..db77d54de 100644 --- a/packages/ui/src/components/Client/DesktopMenu.tsx +++ b/packages/ui/src/components/Client/DesktopMenu.tsx @@ -34,7 +34,7 @@ export function DesktopMenuDropDown({ avoidFocusOnClick()} > diff --git a/packages/ui/src/components/Client/MobileMenu.tsx b/packages/ui/src/components/Client/MobileMenu.tsx index 022402639..e44e0f829 100644 --- a/packages/ui/src/components/Client/MobileMenu.tsx +++ b/packages/ui/src/components/Client/MobileMenu.tsx @@ -97,7 +97,7 @@ export function MobileMenuDropdown({ className={clsx( 'flex w-full items-center justify-between py-4 leading-[1.25rem]', open && 'text-blue-600', - !open && 'text-gray-600', + !open && 'text-gray-900', open && nestLevel === 1 && 'bg-blue-100', !open && nestLevel === 1 && 'border-b border-b-blue-100', nestLevel === 1 && 'px-8 text-lg', diff --git a/packages/ui/src/components/Molecules/Breadcrumbs.stories.tsx b/packages/ui/src/components/Molecules/Breadcrumbs.stories.tsx index 9a7c6d0e5..6fafb4f0a 100644 --- a/packages/ui/src/components/Molecules/Breadcrumbs.stories.tsx +++ b/packages/ui/src/components/Molecules/Breadcrumbs.stories.tsx @@ -1,5 +1,5 @@ import { FrameQuery, OperationExecutor } from '@custom/schema'; -import { Meta } from '@storybook/react'; +import { Meta, StoryObj } from '@storybook/react'; import React from 'react'; import { Default as FrameStory } from '../Routes/Frame.stories'; @@ -9,11 +9,7 @@ export default { component: BreadCrumbs, parameters: { layout: 'fullscreen', - location: new URL('local:/gatsby-turbo'), }, -} satisfies Meta; - -export const Default = { render: () => { return ( @@ -21,4 +17,16 @@ export const Default = { ); }, +} satisfies Meta; + +export const Simple = { + parameters: { + location: new URL('local:/gatsby-turbo'), + }, +}; + +export const Truncated: StoryObj = { + parameters: { + location: new URL('local:/gatsby-turbo-more-more-more'), + }, }; diff --git a/packages/ui/src/components/Molecules/Breadcrumbs.tsx b/packages/ui/src/components/Molecules/Breadcrumbs.tsx index 431291137..6d17d7194 100644 --- a/packages/ui/src/components/Molecules/Breadcrumbs.tsx +++ b/packages/ui/src/components/Molecules/Breadcrumbs.tsx @@ -1,13 +1,25 @@ import { Link } from '@custom/schema'; -import { ChevronRightIcon } from '@heroicons/react/24/outline'; +import { + ChevronRightIcon, + EllipsisHorizontalIcon, +} from '@heroicons/react/24/outline'; import clsx from 'clsx'; -import React from 'react'; +import React, { useEffect, useState } from 'react'; import { isTruthy } from '../../utils/isTruthy'; +import { truncateString } from '../../utils/stringTruncation'; import { useBreadcrumbs } from '../Routes/Menu'; export function BreadCrumbs() { const breadcrumbs = useBreadcrumbs(); + const [hideInnerBreadcrumbs, setHideInnerBreadcrumbs] = useState(false); + const [toggleMoreBreadcrumbs, setToggleMoreBreadcrumbs] = useState(false); + + useEffect(() => { + breadcrumbs.length > 5 && + toggleMoreBreadcrumbs === false && + setHideInnerBreadcrumbs(true); + }, [hideInnerBreadcrumbs, breadcrumbs, toggleMoreBreadcrumbs]); if (!breadcrumbs.length) { return null; @@ -16,38 +28,91 @@ export function BreadCrumbs() { return (
+ ) : null} + 0 && + index < breadcrumbs.length - 1 && + 'hidden', + )} + > + {target === '/' && ( + + )} + + {truncateString({ value: title, maxChar: 20 })} + + + + ))} diff --git a/packages/ui/src/components/Molecules/FadeUp.tsx b/packages/ui/src/components/Molecules/FadeUp.tsx new file mode 100644 index 000000000..3c592af7e --- /dev/null +++ b/packages/ui/src/components/Molecules/FadeUp.tsx @@ -0,0 +1,47 @@ +'use client'; +import clsx from 'clsx'; +import { motion } from 'framer-motion'; +import React, { PropsWithChildren, useEffect, useState } from 'react'; + +export function FadeUp({ + yGap, + children, + className, +}: PropsWithChildren<{ yGap: number; className?: string }>) { + const [reducedMotion, setReducedMotion] = useState(false); + + useEffect(() => { + const mediaQuery = window.matchMedia('(prefers-reduced-motion: reduce)'); + + const handleChange = () => { + setReducedMotion(mediaQuery.matches); + }; + + // Set the initial value + handleChange(); + + // Add the event listener + mediaQuery.addEventListener('change', handleChange); + + return () => { + // Clean up the event listener on unmount + mediaQuery.removeEventListener('change', handleChange); + }; + }, [reducedMotion]); + + console.log('reducedMotion:', reducedMotion); + return ( + + {children} + + ); +} diff --git a/packages/ui/src/components/Molecules/LanguageSwitcher.tsx b/packages/ui/src/components/Molecules/LanguageSwitcher.tsx index 234c27439..5dc030e15 100644 --- a/packages/ui/src/components/Molecules/LanguageSwitcher.tsx +++ b/packages/ui/src/components/Molecules/LanguageSwitcher.tsx @@ -1,30 +1,90 @@ import { Link, Locale, useLocation } from '@custom/schema'; +import { Menu, Transition } from '@headlessui/react'; +import { ChevronDownIcon } from '@heroicons/react/20/solid'; import clsx from 'clsx'; -import React from 'react'; +import React, { Fragment } from 'react'; import { useTranslations } from '../../utils/translations'; +function getLanguageName(locale: string) { + const languageNames = new Intl.DisplayNames([locale], { + type: 'language', + }); + return languageNames.of(locale); +} + export function LanguageSwitcher() { const translations = useTranslations(); const [location] = useLocation(); + return ( -
    - {Object.values(Locale).map((locale) => ( -
  • - {translations[locale] ? ( - - {locale} - - ) : ( - {locale} - )} -
  • - ))} -
+
+ +
+ {Object.values(Locale).map((locale) => ( + + {translations[locale] && + location.pathname !== translations[locale] ? null : ( + + {getLanguageName(locale)} + + )} + + ))} +
+ + + +
+ {Object.values(Locale).map((locale) => ( + + {translations[locale] && + location.pathname !== translations[locale] ? ( + + {({ focus }) => + translations[locale] ? ( + + {getLanguageName(locale as string)} + + ) : ( + + {getLanguageName(locale as string)} + + ) + } + + ) : null} + + ))} +
+
+
+
+
); } diff --git a/packages/ui/src/components/Organisms/Footer.tsx b/packages/ui/src/components/Organisms/Footer.tsx index 73dc34bdf..9db0493f1 100644 --- a/packages/ui/src/components/Organisms/Footer.tsx +++ b/packages/ui/src/components/Organisms/Footer.tsx @@ -22,7 +22,7 @@ export function Footer() {