From c1b753c795d37b8e5d5147b77ce3384bc98c07ad Mon Sep 17 00:00:00 2001 From: Alex Hawley Date: Sat, 26 Aug 2023 20:27:38 -0500 Subject: [PATCH] (Example) Add CMS Sitecore XM Cloud Example (#54535) ### What? This PR adds the `cms-sitecore-xmcloud` example. Sitecore XM Cloud is a headless CMS platform that empowers content editors to manage content, layout, and structural aspects of web pages. The Sitecore JSS framework connects frontend JavaScript applications to the XM Cloud API, allowing retrieval of content and presentation layout information. Sitecore JSS for Next.js integrates Next.js with Sitecore JSS, offering a structured approach to connect a Next.js application to XM Cloud. The `cms-sitecore-xmcloud` example, derived from the Sitecore JSS initializer, showcases the frontend application exclusively, omitting backend implementation details. ### Why? While Sitecore offers detailed documentation for creating XM Cloud projects and JSS applications, existing starter templates often combine frontend and backend configurations. The `cms-sitecore-xmcloud` example focuses solely on the frontend application and includes environment variable examples to establish a connection with XM Cloud, eliminating the need for a .NET-compatible machine. ### How? For comprehensive guidance on setting up an XM Cloud site and JSS application, refer to Sitecore and Vercel documentation. The `cms-sitecore-xmcloud` example is created using the Sitecore JSS initializer, integrating Next.js and SXA (Sitecore Experience Accelerator) add-ons. Further documentation can be found at: - [Deploying to Vercel](https://doc.sitecore.com/xmc/en/developers/xm-cloud/walkthrough--deploying-your-front-end-application-to-vercel.html) - [Documentation (Experience Platform)](https://doc.sitecore.com/xp/en/developers/hd/210/sitecore-headless-development/sitecore-javascript-rendering-sdk--jss--for-next-js.html) - [Documentation (XM Cloud)](https://doc.sitecore.com/xmc/en/developers/xm-cloud/sitecore-javascript-rendering-sdk--jss--for-next-js.html) - [Documentation (Create an XM Cloud project from a starter template)](https://doc.sitecore.com/xmc/en/developers/xm-cloud/create-an-xm-cloud-project-from-a-starter-template-in-the-xm-cloud-deploy-app.html) Co-authored-by: Steven Tey <28986134+steven-tey@users.noreply.github.com> Co-authored-by: JJ Kasper <22380829+ijjk@users.noreply.github.com> --- examples/cms-sitecore-xmcloud/.env.example | 60 + examples/cms-sitecore-xmcloud/.gitignore | 33 + .../cms-sitecore-xmcloud/.graphql-let.yml | 8 + examples/cms-sitecore-xmcloud/LICENSE.txt | 202 + examples/cms-sitecore-xmcloud/README.md | 64 + examples/cms-sitecore-xmcloud/next-env.d.ts | 5 + examples/cms-sitecore-xmcloud/next.config.js | 69 + examples/cms-sitecore-xmcloud/package.json | 89 + .../cms-sitecore-xmcloud/public/favicon.ico | Bin 0 -> 15086 bytes .../cms-sitecore-xmcloud/public/sc_logo.svg | 20 + .../cms-sitecore-xmcloud/scripts/bootstrap.ts | 21 + .../scripts/config/index.ts | 39 + .../scripts/config/plugins/computed.ts | 21 + .../scripts/config/plugins/fallback.ts | 19 + .../scripts/config/plugins/package-json.ts | 22 + .../scripts/config/plugins/scjssconfig.ts | 29 + .../fetch-graphql-introspection-data.ts | 48 + .../scripts/generate-component-factory.ts | 97 + .../scripts/generate-config.ts | 63 + .../scripts/generate-plugins.ts | 138 + .../scripts/scaffold-component.ts | 95 + .../scripts/temp/.npmignore | 2 + .../scripts/temp/config-plugins.ts | 4 + .../scripts/templates/component-factory.ts | 142 + .../scripts/templates/component-src.ts | 27 + .../cms-sitecore-xmcloud/scripts/utils.ts | 64 + .../config/xmcloud-nextjs-starter.config | 165 + examples/cms-sitecore-xmcloud/src/Layout.tsx | 63 + .../cms-sitecore-xmcloud/src/Navigation.tsx | 27 + .../cms-sitecore-xmcloud/src/NotFound.tsx | 19 + examples/cms-sitecore-xmcloud/src/Scripts.tsx | 5 + .../cms-sitecore-xmcloud/src/assets/app.css | 39 + .../src/assets/basic/_component.scss | 18 + .../src/assets/basic/_container.scss | 79 + .../src/assets/basic/_fonts.scss | 1 + .../src/assets/basic/_footer.scss | 32 + .../src/assets/basic/_header.scss | 49 + .../src/assets/basic/_navigation.scss | 152 + .../src/assets/basic/_promo.scss | 58 + .../src/assets/basic/_rich-text.scss | 11 + .../src/assets/basic/_variables.scss | 10 + .../src/assets/basic/main.scss | 8 + .../cms-sitecore-xmcloud/src/assets/main.scss | 5 + .../src/assets/sass/_app.scss | 105 + .../src/assets/sass/abstracts/_functions.scss | 7 + .../src/assets/sass/abstracts/_mixins.scss | 121 + .../src/assets/sass/abstracts/_vars.scss | 3 + .../assets/sass/abstracts/vars/_colors.scss | 283 + .../sass/abstracts/vars/_fontSizes.scss | 16 + .../assets/sass/abstracts/vars/_margins.scss | 11 + .../src/assets/sass/base/fonts/_fonts.scss | 1 + .../src/assets/sass/base/fonts/index.scss | 1 + .../src/assets/sass/base/index.scss | 3 + .../assets/sass/base/links/_link-button.scss | 29 + .../src/assets/sass/base/links/index.scss | 1 + .../src/assets/sass/base/reset/_inputs.scss | 67 + .../src/assets/sass/base/reset/_links.scss | 14 + .../sass/base/reset/_ui-datepicker.scss | 7 + .../base/richtext/_richtext-files-icons.scss | 203 + .../assets/sass/base/richtext/_richtext.scss | 101 + .../src/assets/sass/base/richtext/index.scss | 2 + .../assets/sass/base/typehead/_typehead.scss | 95 + .../src/assets/sass/base/typehead/index.scss | 1 + .../_component-column-splitter.scss | 14 + .../sass/components/_component-container.scss | 21 + .../sass/components/_component-image.scss | 18 + .../components/_component-navigation.scss | 54 + .../sass/components/_component-promo.scss | 42 + .../_component-richtext-content.scss | 20 + .../sass/components/common/_alignment.scss | 26 + .../assets/sass/components/common/_boxed.scss | 16 + .../sass/components/common/_clearfix.scss | 11 + .../sass/components/common/_highlighted.scss | 63 + .../sass/components/common/_link-button.scss | 18 + .../sass/components/common/_promoted-box.scss | 3 + .../assets/sass/components/common/index.scss | 6 + .../sass/components/container/_bordered.scss | 24 + .../components/container/_title-row-box.scss | 69 + .../sass/components/container/index.scss | 1 + .../image-alignment/_image-left.scss | 3 + .../image-alignment/_image-right.scss | 3 + .../sass/components/image/_image-banner.scss | 15 + .../components/image/_image-default-size.scss | 6 + .../assets/sass/components/image/index.scss | 2 + .../src/assets/sass/components/index.scss | 17 + .../sass/components/layout/_acaindent.scss | 5 + .../sass/components/layout/_background.scss | 27 + .../assets/sass/components/layout/index.scss | 1 + .../link-list/_component-link-list.scss | 50 + .../components/link-list/_list-vertical.scss | 19 + .../sass/components/link-list/index.scss | 2 + .../navigation/_navigation-fat.scss | 57 + .../_navigation-main-horizontal-vertical.scss | 158 + .../navigation/_navigation-mobile.scss | 89 + .../navigation/_navigation-sidebar.scss | 29 + .../navigation/_sitemap-navigation.scss | 20 + .../sass/components/navigation/index.scss | 5 + .../promo/_absolute-bottom-link.scss | 8 + .../sass/components/promo/_promo-hero.scss | 42 + .../sass/components/promo/_promo-shadow.scss | 43 + .../assets/sass/components/promo/index.scss | 3 + .../rich-text/_rich-text-lists.scss | 63 + .../sass/components/rich-text/index.scss | 1 + .../spacing/_background-colors.scss | 14 + .../sass/components/spacing/_indent.scss | 10 + .../assets/sass/components/spacing/index.scss | 2 + .../components/title/_component-title.scss | 25 + .../assets/sass/components/title/index.scss | 1 + .../src/assets/sass/main.scss | 4 + .../src/assets/sass/variants/index.scss | 6 + .../assets/sass/variants/link-list/index.scss | 0 .../sass/variants/navigation/index.scss | 0 .../sass/variants/page-content/index.scss | 0 .../src/assets/sass/variants/promo/index.scss | 0 .../assets/sass/variants/rich-text/index.scss | 0 .../src/assets/sass/variants/title/index.scss | 0 .../src/components/ColumnSplitter.tsx | 65 + .../src/components/Container.tsx | 67 + .../src/components/ContentBlock.tsx | 29 + .../src/components/FEaaSWrapper.tsx | 18 + .../src/components/Image.tsx | 97 + .../src/components/LinkList.tsx | 92 + .../src/components/Navigation.tsx | 170 + .../src/components/PageContent.tsx | 68 + .../PartialDesignDynamicPlaceholder.tsx | 20 + .../src/components/Promo.tsx | 90 + .../src/components/RichText.tsx | 32 + .../src/components/RowSplitter.tsx | 55 + .../src/components/Title.tsx | 101 + .../src/lib/component-props/index.ts | 23 + .../src/lib/data-fetcher.ts | 18 + .../src/lib/dictionary-service-factory.ts | 39 + .../src/lib/extract-path/index.ts | 38 + .../src/lib/layout-service-factory.ts | 34 + .../src/lib/middleware/index.ts | 32 + .../src/lib/middleware/plugins/redirects.ts | 40 + .../src/lib/next-config/plugins/graphql.js | 35 + .../src/lib/next-config/plugins/robots.js | 19 + .../src/lib/next-config/plugins/sass.js | 22 + .../src/lib/next-config/plugins/sitemap.js | 19 + .../src/lib/page-props-factory/index.ts | 50 + .../plugins/component-props.ts | 43 + .../page-props-factory/plugins/normal-mode.ts | 88 + .../plugins/preview-mode.ts | 34 + .../lib/page-props-factory/plugins/site.ts | 23 + .../src/lib/page-props.ts | 18 + .../src/lib/site-resolver/index.ts | 28 + .../src/lib/site-resolver/plugins/default.ts | 18 + .../src/lib/sitemap-fetcher/index.ts | 27 + .../plugins/graphql-sitemap-service.ts | 31 + .../cms-sitecore-xmcloud/src/middleware.ts | 19 + .../cms-sitecore-xmcloud/src/pages/404.tsx | 55 + .../cms-sitecore-xmcloud/src/pages/500.tsx | 74 + .../src/pages/[[...path]].tsx | 114 + .../cms-sitecore-xmcloud/src/pages/_app.tsx | 23 + .../cms-sitecore-xmcloud/src/pages/_error.tsx | 35 + .../src/pages/api/editing/data/[key].ts | 25 + .../src/pages/api/editing/render.ts | 31 + .../src/pages/api/healthz.ts | 12 + .../src/pages/api/robots.ts | 29 + .../src/pages/api/sitemap.ts | 79 + .../cms-sitecore-xmcloud/src/temp/.gitignore | 3 + .../src/temp/GraphQLIntrospectionResult.json | 22205 ++++++++++++++++ examples/cms-sitecore-xmcloud/tsconfig.json | 38 + .../tsconfig.scripts.json | 9 + 165 files changed, 28663 insertions(+) create mode 100644 examples/cms-sitecore-xmcloud/.env.example create mode 100644 examples/cms-sitecore-xmcloud/.gitignore create mode 100644 examples/cms-sitecore-xmcloud/.graphql-let.yml create mode 100644 examples/cms-sitecore-xmcloud/LICENSE.txt create mode 100644 examples/cms-sitecore-xmcloud/README.md create mode 100644 examples/cms-sitecore-xmcloud/next-env.d.ts create mode 100644 examples/cms-sitecore-xmcloud/next.config.js create mode 100644 examples/cms-sitecore-xmcloud/package.json create mode 100644 examples/cms-sitecore-xmcloud/public/favicon.ico create mode 100644 examples/cms-sitecore-xmcloud/public/sc_logo.svg create mode 100644 examples/cms-sitecore-xmcloud/scripts/bootstrap.ts create mode 100644 examples/cms-sitecore-xmcloud/scripts/config/index.ts create mode 100644 examples/cms-sitecore-xmcloud/scripts/config/plugins/computed.ts create mode 100644 examples/cms-sitecore-xmcloud/scripts/config/plugins/fallback.ts create mode 100644 examples/cms-sitecore-xmcloud/scripts/config/plugins/package-json.ts create mode 100644 examples/cms-sitecore-xmcloud/scripts/config/plugins/scjssconfig.ts create mode 100644 examples/cms-sitecore-xmcloud/scripts/fetch-graphql-introspection-data.ts create mode 100644 examples/cms-sitecore-xmcloud/scripts/generate-component-factory.ts create mode 100644 examples/cms-sitecore-xmcloud/scripts/generate-config.ts create mode 100644 examples/cms-sitecore-xmcloud/scripts/generate-plugins.ts create mode 100644 examples/cms-sitecore-xmcloud/scripts/scaffold-component.ts create mode 100644 examples/cms-sitecore-xmcloud/scripts/temp/.npmignore create mode 100644 examples/cms-sitecore-xmcloud/scripts/temp/config-plugins.ts create mode 100644 examples/cms-sitecore-xmcloud/scripts/templates/component-factory.ts create mode 100644 examples/cms-sitecore-xmcloud/scripts/templates/component-src.ts create mode 100644 examples/cms-sitecore-xmcloud/scripts/utils.ts create mode 100644 examples/cms-sitecore-xmcloud/sitecore/config/xmcloud-nextjs-starter.config create mode 100644 examples/cms-sitecore-xmcloud/src/Layout.tsx create mode 100644 examples/cms-sitecore-xmcloud/src/Navigation.tsx create mode 100644 examples/cms-sitecore-xmcloud/src/NotFound.tsx create mode 100644 examples/cms-sitecore-xmcloud/src/Scripts.tsx create mode 100644 examples/cms-sitecore-xmcloud/src/assets/app.css create mode 100644 examples/cms-sitecore-xmcloud/src/assets/basic/_component.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/basic/_container.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/basic/_fonts.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/basic/_footer.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/basic/_header.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/basic/_navigation.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/basic/_promo.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/basic/_rich-text.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/basic/_variables.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/basic/main.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/main.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/_app.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/abstracts/_functions.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/abstracts/_mixins.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/abstracts/_vars.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/abstracts/vars/_colors.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/abstracts/vars/_fontSizes.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/abstracts/vars/_margins.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/base/fonts/_fonts.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/base/fonts/index.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/base/index.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/base/links/_link-button.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/base/links/index.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/base/reset/_inputs.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/base/reset/_links.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/base/reset/_ui-datepicker.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/base/richtext/_richtext-files-icons.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/base/richtext/_richtext.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/base/richtext/index.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/base/typehead/_typehead.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/base/typehead/index.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/components/_component-column-splitter.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/components/_component-container.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/components/_component-image.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/components/_component-navigation.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/components/_component-promo.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/components/_component-richtext-content.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/components/common/_alignment.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/components/common/_boxed.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/components/common/_clearfix.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/components/common/_highlighted.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/components/common/_link-button.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/components/common/_promoted-box.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/components/common/index.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/components/container/_bordered.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/components/container/_title-row-box.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/components/container/index.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/components/image-alignment/_image-left.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/components/image-alignment/_image-right.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/components/image/_image-banner.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/components/image/_image-default-size.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/components/image/index.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/components/index.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/components/layout/_acaindent.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/components/layout/_background.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/components/layout/index.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/components/link-list/_component-link-list.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/components/link-list/_list-vertical.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/components/link-list/index.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/components/navigation/_navigation-fat.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/components/navigation/_navigation-main-horizontal-vertical.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/components/navigation/_navigation-mobile.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/components/navigation/_navigation-sidebar.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/components/navigation/_sitemap-navigation.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/components/navigation/index.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/components/promo/_absolute-bottom-link.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/components/promo/_promo-hero.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/components/promo/_promo-shadow.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/components/promo/index.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/components/rich-text/_rich-text-lists.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/components/rich-text/index.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/components/spacing/_background-colors.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/components/spacing/_indent.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/components/spacing/index.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/components/title/_component-title.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/components/title/index.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/main.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/variants/index.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/variants/link-list/index.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/variants/navigation/index.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/variants/page-content/index.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/variants/promo/index.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/variants/rich-text/index.scss create mode 100644 examples/cms-sitecore-xmcloud/src/assets/sass/variants/title/index.scss create mode 100644 examples/cms-sitecore-xmcloud/src/components/ColumnSplitter.tsx create mode 100644 examples/cms-sitecore-xmcloud/src/components/Container.tsx create mode 100644 examples/cms-sitecore-xmcloud/src/components/ContentBlock.tsx create mode 100644 examples/cms-sitecore-xmcloud/src/components/FEaaSWrapper.tsx create mode 100644 examples/cms-sitecore-xmcloud/src/components/Image.tsx create mode 100644 examples/cms-sitecore-xmcloud/src/components/LinkList.tsx create mode 100644 examples/cms-sitecore-xmcloud/src/components/Navigation.tsx create mode 100644 examples/cms-sitecore-xmcloud/src/components/PageContent.tsx create mode 100644 examples/cms-sitecore-xmcloud/src/components/PartialDesignDynamicPlaceholder.tsx create mode 100644 examples/cms-sitecore-xmcloud/src/components/Promo.tsx create mode 100644 examples/cms-sitecore-xmcloud/src/components/RichText.tsx create mode 100644 examples/cms-sitecore-xmcloud/src/components/RowSplitter.tsx create mode 100644 examples/cms-sitecore-xmcloud/src/components/Title.tsx create mode 100644 examples/cms-sitecore-xmcloud/src/lib/component-props/index.ts create mode 100644 examples/cms-sitecore-xmcloud/src/lib/data-fetcher.ts create mode 100644 examples/cms-sitecore-xmcloud/src/lib/dictionary-service-factory.ts create mode 100644 examples/cms-sitecore-xmcloud/src/lib/extract-path/index.ts create mode 100644 examples/cms-sitecore-xmcloud/src/lib/layout-service-factory.ts create mode 100644 examples/cms-sitecore-xmcloud/src/lib/middleware/index.ts create mode 100644 examples/cms-sitecore-xmcloud/src/lib/middleware/plugins/redirects.ts create mode 100644 examples/cms-sitecore-xmcloud/src/lib/next-config/plugins/graphql.js create mode 100644 examples/cms-sitecore-xmcloud/src/lib/next-config/plugins/robots.js create mode 100644 examples/cms-sitecore-xmcloud/src/lib/next-config/plugins/sass.js create mode 100644 examples/cms-sitecore-xmcloud/src/lib/next-config/plugins/sitemap.js create mode 100644 examples/cms-sitecore-xmcloud/src/lib/page-props-factory/index.ts create mode 100644 examples/cms-sitecore-xmcloud/src/lib/page-props-factory/plugins/component-props.ts create mode 100644 examples/cms-sitecore-xmcloud/src/lib/page-props-factory/plugins/normal-mode.ts create mode 100644 examples/cms-sitecore-xmcloud/src/lib/page-props-factory/plugins/preview-mode.ts create mode 100644 examples/cms-sitecore-xmcloud/src/lib/page-props-factory/plugins/site.ts create mode 100644 examples/cms-sitecore-xmcloud/src/lib/page-props.ts create mode 100644 examples/cms-sitecore-xmcloud/src/lib/site-resolver/index.ts create mode 100644 examples/cms-sitecore-xmcloud/src/lib/site-resolver/plugins/default.ts create mode 100644 examples/cms-sitecore-xmcloud/src/lib/sitemap-fetcher/index.ts create mode 100644 examples/cms-sitecore-xmcloud/src/lib/sitemap-fetcher/plugins/graphql-sitemap-service.ts create mode 100644 examples/cms-sitecore-xmcloud/src/middleware.ts create mode 100644 examples/cms-sitecore-xmcloud/src/pages/404.tsx create mode 100644 examples/cms-sitecore-xmcloud/src/pages/500.tsx create mode 100644 examples/cms-sitecore-xmcloud/src/pages/[[...path]].tsx create mode 100644 examples/cms-sitecore-xmcloud/src/pages/_app.tsx create mode 100644 examples/cms-sitecore-xmcloud/src/pages/_error.tsx create mode 100644 examples/cms-sitecore-xmcloud/src/pages/api/editing/data/[key].ts create mode 100644 examples/cms-sitecore-xmcloud/src/pages/api/editing/render.ts create mode 100644 examples/cms-sitecore-xmcloud/src/pages/api/healthz.ts create mode 100644 examples/cms-sitecore-xmcloud/src/pages/api/robots.ts create mode 100644 examples/cms-sitecore-xmcloud/src/pages/api/sitemap.ts create mode 100644 examples/cms-sitecore-xmcloud/src/temp/.gitignore create mode 100644 examples/cms-sitecore-xmcloud/src/temp/GraphQLIntrospectionResult.json create mode 100644 examples/cms-sitecore-xmcloud/tsconfig.json create mode 100644 examples/cms-sitecore-xmcloud/tsconfig.scripts.json diff --git a/examples/cms-sitecore-xmcloud/.env.example b/examples/cms-sitecore-xmcloud/.env.example new file mode 100644 index 000000000000..12b0dbb5b0a6 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/.env.example @@ -0,0 +1,60 @@ +# Read the Vercel + Sitecore docs for the full setup instructions: https://vercel.com/docs/integrations/sitecore + +# For development purposes, note Next.js supports a .env.local +# file, which is already configured to be git ignored. +# Read more about Next.js support of environment variables here: +# https://nextjs.org/docs/basic-features/environment-variables +JSS_APP_NAME= + +# The public URL to use for absolute URLs, which are required when +# the Next.js app is run within Sitecore editors. +# This should match the `serverSideRenderingEngineApplicationUrl` +# in your Sitecore configuration (see \sitecore\config\xmcloud-nextjs-starter.config). +# Be sure to update these values accordingly as your public endpoint changes. +# See https://jss.sitecore.com/docs/fundamentals/services/view-engine +PUBLIC_URL=http://localhost:3000 + +# To secure the Sitecore editor endpoint exposed by your Next.js app +# (`/api/editing/render` by default), a secret token is used. This (client-side) +# value must match your server-side value (see \sitecore\config\xmcloud-nextjs-starter.config). +# We recommend an alphanumeric value of at least 16 characters. +JSS_EDITING_SECRET= + +# Your Sitecore API key is needed to build the app. Typically, the API key is +# defined in `scjssconfig.json` (as `sitecore.apiKey`). This file may not exist +# when building locally (if you've never run `jss setup`), or when building in a +# higher environment (since `scjssconfig.json` is ignored from source control). +# In this case, use this environment variable to provide the value at build time. +SITECORE_API_KEY= + +# Your Sitecore API hostname is needed to build the app. Typically, the API host is +# defined in `scjssconfig.json` (as `sitecore.layoutServiceHost`). This file may +# not exist when building locally (if you've never run `jss setup`), or when building +# in a higher environment (since `scjssconfig.json` is ignored from source control). +# In this case, use this environment variable to provide the value at build time. +SITECORE_API_HOST= + +# Your GraphQL Edge endpoint. This is required for Sitecore Experience Edge. +# For Sitecore XM, this is typically optional. By default, the endpoint is calculated using +# the resolved Sitecore API hostname + the `graphQLEndpointPath` defined in your `package.json`. +GRAPH_QL_ENDPOINT= + +# Your default app language. +DEFAULT_LANGUAGE= + +# The way in which layout and dictionary data is fetched from Sitecore +FETCH_WITH=GraphQL + +# Indicates whether SSG `getStaticPaths` pre-render any pages +# Set the environment variable DISABLE_SSG_FETCH=true +# to enable full ISR (Incremental Static Regeneration) flow +DISABLE_SSG_FETCH= + +# Sitecore JSS npm packages utilize the debug module for debug logging. +# https://www.npmjs.com/package/debug +# Set the DEBUG environment variable to 'sitecore-jss:*' to see all logs: +#DEBUG=sitecore-jss:* +# Or be selective and show for example only layout service logs: +#DEBUG=sitecore-jss:layout +# Or everything BUT layout service logs: +#DEBUG=sitecore-jss:*,-sitecore-jss:layout diff --git a/examples/cms-sitecore-xmcloud/.gitignore b/examples/cms-sitecore-xmcloud/.gitignore new file mode 100644 index 000000000000..b9d296990d87 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/.gitignore @@ -0,0 +1,33 @@ +# See https://help.github.com/ignore-files/ for more about ignoring files. + +# dependencies +/node_modules + +# testing +/coverage + +# next.js +/.next*/ +/out/ + +# graphql code generation +/.generated +*.graphql.d.ts +*.graphqls.d.ts + +# misc +.DS_Store + +# local env files +.env.local +.env.*.local + +# Log files +*.log* + +# sitecore +scjssconfig.json +*.deploysecret.config + +# vercel +.vercel \ No newline at end of file diff --git a/examples/cms-sitecore-xmcloud/.graphql-let.yml b/examples/cms-sitecore-xmcloud/.graphql-let.yml new file mode 100644 index 000000000000..85b6e0d8a2f2 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/.graphql-let.yml @@ -0,0 +1,8 @@ +schema: + - './src/temp/GraphQLIntrospectionResult.json' +documents: 'src/**/*.graphql' +plugins: + - typescript-operations + - typed-document-node +config: + useIndexSignature: true diff --git a/examples/cms-sitecore-xmcloud/LICENSE.txt b/examples/cms-sitecore-xmcloud/LICENSE.txt new file mode 100644 index 000000000000..d64569567334 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/examples/cms-sitecore-xmcloud/README.md b/examples/cms-sitecore-xmcloud/README.md new file mode 100644 index 000000000000..a83926b59e02 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/README.md @@ -0,0 +1,64 @@ +# A Next.js Example Using Sitecore JSS and Sitecore XM Cloud + +This example connects to a Sitecore XM Cloud site using the Sitecore JavaScript Rendering SDK (JSS) for Next.js and includes example components and configuration for headless SXA (Sitecore Experience Accelerator). For more information on creating and deploying a headless Sitecore solution to XM Cloud please refer to [Vercel's Sitecore XM Cloud Integration Guide](https://vercel.com/docs/integrations/sitecore) or official [Sitecore documentation](https://doc.sitecore.com/xmc/en/developers/xm-cloud/create-an-xm-cloud-project-from-a-starter-template-in-the-xm-cloud-deploy-app.html). + +## Demo + +### [https://vercel-sitecore-xmcloud-demo.vercel.app](https://vercel-sitecore-xmcloud-demo.vercel.app) + +## Deploy your own + +Using the Deploy Button below, you'll deploy the Next.js project as well as connect it to your XM Cloud project with the required environment variables. + +[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?demo-title=Sitecore+XM+Cloud+Next.js+Starter&demo-description=Simple+Next.js+blog+site+that+connects+to+a+Sitecore+XM+Cloud+site+using+the+Sitecore+JavaScript+Rendering+SDK+%28JSS%29.&demo-url=https%3A%2F%2Fvercel-sitecore-xmcloud-demo.vercel.app%2F&demo-image=%2F%2Fimages.ctfassets.net%2Fe5382hct74si%2FJAWlcS27EakxvDFRjmLwD%2F412631142afd83d7b3a926cb7c3e44bd%2FCleanShot_2023-08-25_at_20.09.25_2x.png&project-name=Sitecore+XM+Cloud+Next.js+Starter&repository-name=sitecore-starter&repository-url=https%3A%2F%2Fgithub.com%2Fvercel%2Fnext.js%2Ftree%2Fcanary%2Fexamples%2Fcms-sitecore-xmcloud&from=templates&skippable-integrations=1&env=JSS_APP_NAME%2CPUBLIC_URL%2CJSS_EDITING_SECRET%2CSITECORE_API_KEY%2CSITECORE_API_HOST%2CGRAPH_QL_ENDPOINT%2CDEFAULT_LANGUAGE%2CFETCH_WITH%2CDISABLE_SSG_FETCH&envDescription=Instructions+on+how+to+get+these+env+vars&envLink=https%3A%2F%2Fgithub.com%2Fvercel%2Fnext.js%2Ftree%2Fcanary%2Fexamples%2Fcms-sitecore-xmcloud%2F.env.example) + +- `JSS_APP_NAME`: The name of the JSS app that is configured in XM Cloud. +- `GRAPH_QL_ENDPOINT`: The GraphQL Edge endpoint. This is required for Sitecore Experience Edge. +- `SITECORE_API_KEY`: The Sitecore API key is required to build the app. +- `SITECORE_API_HOST`: The host of the Sitecore API. +- `FETCH_WITH`: The fetch method to the Sitecore API. This can be either `GraphQL` or `REST`. + +### Related examples + +- [AgilityCMS](/examples/cms-agilitycms) +- [Builder.io](/examples/cms-builder-io) +- [ButterCMS](/examples/cms-buttercms) +- [Contentful](/examples/cms-contentful) +- [Cosmic](/examples/cms-cosmic) +- [DatoCMS](/examples/cms-datocms) +- [DotCMS](/examples/cms-dotcms) +- [Drupal](/examples/cms-drupal) +- [Enterspeed](/examples/cms-enterspeed) +- [Ghost](/examples/cms-ghost) +- [GraphCMS](/examples/cms-graphcms) +- [Kontent](/examples/cms-kontent-ai) +- [Prepr](/examples/cms-prepr) +- [Prismic](/examples/cms-prismic) +- [Sanity](/examples/cms-sanity) +- [Sitefinity](/examples/cms-sitefinity) +- [Storyblok](/examples/cms-storyblok) +- [TakeShape](/examples/cms-takeshape) +- [Umbraco heartcore](/examples/cms-umbraco-heartcore) +- [Webiny](/examples/cms-webiny) +- [Blog Starter](/examples/blog-starter) +- [WordPress](/examples/cms-wordpress) + +## How to use + +Execute [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) with [npm](https://docs.npmjs.com/cli/init), [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/), or [pnpm](https://pnpm.io) to bootstrap the example: + +```bash +npx create-next-app --example cms-sitecore-xmcloud cms-sitecore-xmcloud-app +``` + +```bash +yarn create-next-app --example cms-sitecore-xmcloud cms-sitecore-xmcloud-app +``` + +```bash +pnpm create-next-app --example cms-sitecore-xmcloud cms-sitecore-xmcloud-app +``` + +## Configuration + +To configure and run this example you can follow our [Sitecore XM Cloud Integration Guide](https://vercel.com/docs/integrations/sitecore) diff --git a/examples/cms-sitecore-xmcloud/next-env.d.ts b/examples/cms-sitecore-xmcloud/next-env.d.ts new file mode 100644 index 000000000000..4f11a03dc6cc --- /dev/null +++ b/examples/cms-sitecore-xmcloud/next-env.d.ts @@ -0,0 +1,5 @@ +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/examples/cms-sitecore-xmcloud/next.config.js b/examples/cms-sitecore-xmcloud/next.config.js new file mode 100644 index 000000000000..ca92b3473e27 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/next.config.js @@ -0,0 +1,69 @@ +const jssConfig = require('./src/temp/config') +const { getPublicUrl } = require('@sitecore-jss/sitecore-jss-nextjs') +const plugins = require('./src/temp/next-config-plugins') || {} + +const publicUrl = getPublicUrl() + +/** + * @type {import('next').NextConfig} + */ +const nextConfig = { + // Set assetPrefix to our public URL + assetPrefix: publicUrl, + + // Allow specifying a distinct distDir when concurrently running app in a container + distDir: process.env.NEXTJS_DIST_DIR || '.next', + + // Make the same PUBLIC_URL available as an environment variable on the client bundle + env: { + PUBLIC_URL: publicUrl, + }, + + i18n: { + // These are all the locales you want to support in your application. + // These should generally match (or at least be a subset of) those in Sitecore. + locales: ['en'], + // This is the locale that will be used when visiting a non-locale + // prefixed path e.g. `/styleguide`. + defaultLocale: jssConfig.defaultLanguage, + }, + + // Enable React Strict Mode + reactStrictMode: true, + + async rewrites() { + // When in connected mode we want to proxy Sitecore paths off to Sitecore + return [ + // API endpoints + { + source: '/sitecore/api/:path*', + destination: `${jssConfig.sitecoreApiHost}/sitecore/api/:path*`, + }, + // media items + { + source: '/-/:path*', + destination: `${jssConfig.sitecoreApiHost}/-/:path*`, + }, + // visitor identification + { + source: '/layouts/system/:path*', + destination: `${jssConfig.sitecoreApiHost}/layouts/system/:path*`, + }, + // healthz check + { + source: '/healthz', + destination: '/api/healthz', + }, + // rewrite for Sitecore service pages + { + source: '/sitecore/service/:path*', + destination: `${jssConfig.sitecoreApiHost}/sitecore/service/:path*`, + }, + ] + }, +} + +module.exports = () => { + // Run the base config through any configured plugins + return Object.values(plugins).reduce((acc, plugin) => plugin(acc), nextConfig) +} diff --git a/examples/cms-sitecore-xmcloud/package.json b/examples/cms-sitecore-xmcloud/package.json new file mode 100644 index 000000000000..19be60eb7a57 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/package.json @@ -0,0 +1,89 @@ +{ + "private": true, + "config": { + "appName": "xmcloud-nextjs-starter", + "rootPlaceholders": [ + "jss-main" + ], + "sitecoreConfigPath": "/App_Config/Include/zzz", + "graphQLEndpointPath": "/sitecore/api/graph/edge", + "language": "en", + "templates": [ + "nextjs", + "nextjs-sxa" + ] + }, + "engines": { + "node": ">=12", + "npm": ">=6" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/sitecore/jss.git" + }, + "bugs": { + "url": "https://github.com/sitecore/jss/issues" + }, + "dependencies": { + "@sitecore-jss/sitecore-jss-nextjs": "~21.1.6", + "bootstrap": "^5.1.3", + "font-awesome": "^4.7.0", + "graphql": "~15.8.0", + "graphql-tag": "^2.12.6", + "next": "^13.1.6", + "next-localization": "^0.12.0", + "react": "^18.2.0", + "react-dom": "^18.2.0" + }, + "devDependencies": { + "@graphql-codegen/cli": "^1.21.8", + "@graphql-codegen/import-types-preset": "^2.2.6", + "@graphql-codegen/plugin-helpers": "^3.1.2", + "@graphql-codegen/typed-document-node": "^2.3.12", + "@graphql-codegen/typescript": "^2.8.7", + "@graphql-codegen/typescript-operations": "^2.5.12", + "@graphql-codegen/typescript-resolvers": "^2.7.12", + "@graphql-typed-document-node/core": "^3.1.1", + "@sitecore-jss/sitecore-jss-cli": "~21.1.6", + "@types/node": "^18.11.18", + "@types/react": "^18.0.12", + "@types/react-dom": "^18.0.5", + "@typescript-eslint/eslint-plugin": "^5.49.0", + "@typescript-eslint/parser": "^5.49.0", + "chalk": "~4.1.2", + "chokidar": "~3.5.3", + "constant-case": "^3.0.4", + "cross-env": "~7.0.3", + "dotenv": "^16.0.3", + "eslint": "^8.32.0", + "eslint-config-next": "^13.1.5", + "eslint-config-prettier": "^8.6.0", + "eslint-plugin-prettier": "^4.2.1", + "eslint-plugin-yaml": "^0.5.0", + "graphql-let": "^0.18.6", + "npm-run-all": "~4.1.5", + "prettier": "^2.8.3", + "sass": "^1.52.3", + "sass-alias": "^1.0.5", + "ts-node": "^10.9.1", + "tsconfig-paths": "^4.1.2", + "typescript": "~4.9.4", + "yaml-loader": "^0.8.0" + }, + "scripts": { + "dev": "npm run next:dev", + "start": "npm run next:start", + "jss": "jss", + "lint": "eslint ./src/**/*.tsx ./src/**/*.ts ./scripts/**/*.ts", + "bootstrap": "ts-node --project tsconfig.scripts.json scripts/bootstrap.ts", + "build": "npm-run-all --serial bootstrap next:build", + "graphql:update": "ts-node --project tsconfig.scripts.json ./scripts/fetch-graphql-introspection-data.ts", + "next:build": "next build", + "next:dev": "cross-env NODE_OPTIONS='--inspect' next dev", + "next:start": "next start", + "scaffold": "ts-node --project tsconfig.scripts.json scripts/scaffold-component.ts", + "start:connected": "npm-run-all --serial bootstrap --parallel next:dev start:watch-components", + "start:production": "npm-run-all --serial bootstrap next:build next:start", + "start:watch-components": "ts-node --project tsconfig.scripts.json scripts/generate-component-factory.ts --watch" + } +} diff --git a/examples/cms-sitecore-xmcloud/public/favicon.ico b/examples/cms-sitecore-xmcloud/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..065a432e4186a62ed48411fa6ad2c72fafd73f58 GIT binary patch literal 15086 zcmchd36K@V8GvV55DXG1QyLXj5shGWovc_0}-XynTC^SM||7ef9cg!ZY@Z;1 zB5YI2me$lEs}mdoFG6eANZP(~pI=3ndy3uM_8KxIIFzHuIPw%sfQ#W8 za4*gR%Vj3q375j@a2nhQZ$nFKausbxLAP?pK-=GgF|m}7zM|fEuYYfW1yIg-Z8H~) zy~n~8unbD+vd^EPQ%hr@%|KWRrR2R&{naoT{+npqNZxU4&mwpe^s({Ly0lE|E{3Dv zA#e?*+l1%k)E!#Ne(E=d*(GEsYd%~E`d)p`uXd_`8cczk;0|yfST+ePn*r}Za?L11 zokPHV(g4BcbowlH)mdYpyaGl*Qg*t(G4wC+I%xOPFctm@&wy)aVG-x>Gx!?xy?c8O zI1b0<946(z!}IUJ`6xApzf=FMI|pSu!rPFPnau0sGeFyIHyPZ6x57y9KGPo#gR|g# zaGpkiV{~l#+VL2pyaPFY%YvQ{$^Q|?fMX8j$<(#EXL1gB?gZ|mWV>Yk6Y8`{|LN!2 z;o;cS=y?}B>ohkGHpmh{Hc;OK^lNf_A#dN& za5?ze(XJmd)a9ON0N2q~umoJ|?xQE+Q*bP0!wq+ETY`~^z(KZ zp6HCMZg2s(kKNOk!AGFo<3PW=$D9l2XcQ#v4*BJj?*k{os+=~rQ9cO1pD>}_S>&w; zQ$Sm0g1**XZE`HZ#>M211NU?&3#oH<4qTT}XB+Ds8*io^|3mH;;M&mcOF*9o+d`@h zr$Hg3&NkEG@tivS(C#%~YUIe*KkhMYH`cnQg6$!_mNL(Z@SIHNQeKx+_ZsCJm(t(f zI_1NQ;QBos=0nmx^N+yUkQIkK%d*y%{pLW2uKTRRRBYR(XCr7vTdNv)E z)5i67ChU|`Z-38|&Dv{cUVmf{fyI#JTg&v{8IW~$y-)dG&>ilBEE$e*XrBJ|mLt0Y zwg=a^F*9j{?z(|E?X|U3f8=)qW8^cSuca1#4S8#O zSddAZzVIf5u_aT-)!a7p`7ToQYzh~{bl44i&rjMC^6r`Kf(_)INAIwqKAn0#?i<>F zmnnKW!w=v}NZS#f-4|G{I z17$o6n`iayh%#hr`>~LG2TR)SzVQ7yGp19%0Xz!HKI!}{>O1^gMy7lCB*?Q}JLbTq zrF=!bF|`iTb|;_bQGX-R(mH8p3^)@?`6I~o9J~<*LvP3(3w6f19^jd; ze9xA=_#WhYZ$1}V{N9%whwb4k(9c0usZ>3$!EJCp{00t(L&3G?J!~922FsvSoCtdK z-+55>|GlKf(1pIvuQ4mgEH~9Yee1iSciB?wBTJ_9dNQ{zXHIX+K?Y;^2 zhUV}3ZMCzde&}#t>$KcbZ=|`2ZHCi?-7f@ z@feSMe-QVuWuA)SZS4YtZ(}pOjGOdfEuBL@3sx+xw99DY1cTeXIu?9rj1|BSqt4@Pw@Pj4a?yx2>aG^;6s=Lqrm;z z?r(g^@T@%mZh>V`PF%5#apQJyE#u|Z|LC_lOaS+D7-wr!?+BBDzoaYwNBbHuhLqB0 zTyhSir5N=RZT2t60@~@b#gL_YCFR~_9s%z&)8J;f5%jn7pLb7xo4UhF*+9MPW+9|? z8>c*b?*(;)Smr%Qn~Y%#z&JM+w9WOHma~Mifmt?C?mM=!(|x?J+zrl$^Qqi7;RSda zwCgSS0_>|z`p3ANHHVgMp0t5y&(BLCty`bzWA(ibvq0H1;Xg1JoR62G0G8=XnY|5Bk$H@mJ6v2Er*Y2ByNJpg#Tk5NNNm{C?`aNNb1m6JHiRz9u9Tike>htLPc_& zl*-e#8@T4)1^3A^cmSLa*T*IB4g@{=_;i?_D6?)U42D+!-6?&#!wukGydR96pMrAr zi~BR^H~km9k}7k(w)y|(=-UtU`yG&+SMNC2z-kCKSf7qp6H58HoH}H>m))D%5OjS; z-v2KMal&%TlFycnDo01DHptiy9IyL)6)4wK{mJn*@SN6vVJX?=)FHDPTmu(@>)>&i z0p~zkzxy@#$aH);I!d)cMjvo)=YZ>5Cc&wY9IyU&ZzT154{nY%rS#<0(SI*+?I-0L zAFl%UcPM{@yfH33n>ue>=E-iUoW9+_u_k4jHxAsHD!-fOp%B)H?X+)bOM3F^>DvQb z|DQmx;Y;!(!8r+K#>G=$YtRp&+_V7e=jmvvoW5P)Ab1Si@Atvqius^tG0)pW1&oDI zZrWs9OL|(WN6rRtBK#H>LQ==IJo84-bO!E5K7BqJ$IH62m9hgF8-n9c>h^5kJM<&p z2|VwTZOnU~^$+b@o6=X^-Vc*8SGi-LY3xBJ&zperp0+{19SvpgN!mK6JA>z6TDNzE z&4S)!O8M@PUW?je%$N!_koz~=)OCh!;9OV&>9H)LtfW7wKL9+x(tUk4rpy5M(hwK` z{b3Lo+l(dZO^?~W>OMF*-n=~R_5fpNmYiT$BlRobb4bRIP@hbnp-owLsbCZB`+(~t zd0r%CmXkLIjfd(Ww>2sKj3fKN6mUO&11;&b%~H4toS)q9*5%rQjCH{Ed^S7`+OiUy z1N9kS)%hs=6?TQPzB|Qre1NE}X!=aUHSW;m6?kz6Q_obHMerEgTNs^`3{mBL{nOAsE2Z8s%>erwH^P|h@7M=t z^$TUx_l5gG8B@XaVZSAtCt?{qv8yM+Ea1U6% z0`7uez%H;842DVYG1$g&Y?2%)&l|&4pl!BsPPNfKVgKkq*KO})-;k%=JL*qB{qKV@ z%>DZT+zp4pT4QUd;@c^sD6sNs@!S}wDy*TVj-XPcigd~<>OYO5C!Sc-$G-mqH(reZ literal 0 HcmV?d00001 diff --git a/examples/cms-sitecore-xmcloud/public/sc_logo.svg b/examples/cms-sitecore-xmcloud/public/sc_logo.svg new file mode 100644 index 000000000000..1be7e78814a1 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/public/sc_logo.svg @@ -0,0 +1,20 @@ + + + + Logo + Created with Sketch. + + + + + + \ No newline at end of file diff --git a/examples/cms-sitecore-xmcloud/scripts/bootstrap.ts b/examples/cms-sitecore-xmcloud/scripts/bootstrap.ts new file mode 100644 index 000000000000..7ef1ffeceac2 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/scripts/bootstrap.ts @@ -0,0 +1,21 @@ +/* + BOOTSTRAPPING + The bootstrap process runs before build, and generates JS that needs to be + included into the build - specifically, plugins, the global config module, + and the component name to component mapping. +*/ + +/* + PLUGINS GENERATION +*/ +import './generate-plugins' + +/* + CONFIG GENERATION +*/ +import './generate-config' + +/* + COMPONENT FACTORY GENERATION +*/ +import './generate-component-factory' diff --git a/examples/cms-sitecore-xmcloud/scripts/config/index.ts b/examples/cms-sitecore-xmcloud/scripts/config/index.ts new file mode 100644 index 000000000000..932cf3e42dbc --- /dev/null +++ b/examples/cms-sitecore-xmcloud/scripts/config/index.ts @@ -0,0 +1,39 @@ +// eslint-disable-next-line @typescript-eslint/no-var-requires +const plugins = require('scripts/temp/config-plugins') + +/** + * JSS configuration object + */ +export interface JssConfig extends Record { + sitecoreApiKey?: string + sitecoreApiHost?: string + jssAppName?: string + graphQLEndpointPath?: string + defaultLanguage?: string + graphQLEndpoint?: string +} + +export interface ConfigPlugin { + /** + * Detect order when the plugin should be called, e.g. 0 - will be called first (can be a plugin which data is required for other plugins) + */ + order: number + /** + * A function which will be called during config generation + * @param {JssConfig} config Current (accumulated) config + */ + exec(config: JssConfig): Promise +} + +export class JssConfigFactory { + public async create(defaultConfig: JssConfig = {}): Promise { + return (Object.values(plugins) as ConfigPlugin[]) + .sort((p1, p2) => p1.order - p2.order) + .reduce( + (promise, plugin) => promise.then((config) => plugin.exec(config)), + Promise.resolve(defaultConfig) + ) + } +} + +export const jssConfigFactory = new JssConfigFactory() diff --git a/examples/cms-sitecore-xmcloud/scripts/config/plugins/computed.ts b/examples/cms-sitecore-xmcloud/scripts/config/plugins/computed.ts new file mode 100644 index 000000000000..2a898db95f07 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/scripts/config/plugins/computed.ts @@ -0,0 +1,21 @@ +import { ConfigPlugin, JssConfig } from '..' + +/** + * This plugin will set computed config props. + * The "graphQLEndpoint" is an example of making a _computed_ config setting + * based on other config settings. + */ +class ComputedPlugin implements ConfigPlugin { + // should come after other plugins (but before fallback) + order = 10 + + async exec(config: JssConfig) { + return Object.assign({}, config, { + graphQLEndpoint: + config.graphQLEndpoint || + `${config.sitecoreApiHost}${config.graphQLEndpointPath}`, + }) + } +} + +export const computedPlugin = new ComputedPlugin() diff --git a/examples/cms-sitecore-xmcloud/scripts/config/plugins/fallback.ts b/examples/cms-sitecore-xmcloud/scripts/config/plugins/fallback.ts new file mode 100644 index 000000000000..d428dca3cc52 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/scripts/config/plugins/fallback.ts @@ -0,0 +1,19 @@ +import { ConfigPlugin, JssConfig } from '..' + +/** + * This config will set fallback values for properties that were left empty + * If neither env, nor other places had a proper value, this will ensure a fallback is set + */ +class FallbackPlugin implements ConfigPlugin { + // should always come last + order = 100 + + async exec(config: JssConfig) { + return Object.assign({}, config, { + defaultLanguage: config.defaultLanguage || 'en', + sitecoreApiKey: config.sitecoreApiKey || 'no-api-key-set', + }) + } +} + +export const fallbackPlugin = new FallbackPlugin() diff --git a/examples/cms-sitecore-xmcloud/scripts/config/plugins/package-json.ts b/examples/cms-sitecore-xmcloud/scripts/config/plugins/package-json.ts new file mode 100644 index 000000000000..7b767c98a198 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/scripts/config/plugins/package-json.ts @@ -0,0 +1,22 @@ +import { ConfigPlugin, JssConfig } from '..' +import packageConfig from 'package.json' + +/** + * This plugin will set config props based on package.json. + */ +class PackageJsonPlugin implements ConfigPlugin { + order = 1 + + async exec(config: JssConfig) { + if (!packageConfig.config) return config + + return Object.assign({}, config, { + jssAppName: config.jssAppName || packageConfig.config.appName, + graphQLEndpointPath: + config.graphQLEndpointPath || packageConfig.config.graphQLEndpointPath, + defaultLanguage: config.defaultLanguage || packageConfig.config.language, + }) + } +} + +export const packageJsonPlugin = new PackageJsonPlugin() diff --git a/examples/cms-sitecore-xmcloud/scripts/config/plugins/scjssconfig.ts b/examples/cms-sitecore-xmcloud/scripts/config/plugins/scjssconfig.ts new file mode 100644 index 000000000000..47c5e8d157ed --- /dev/null +++ b/examples/cms-sitecore-xmcloud/scripts/config/plugins/scjssconfig.ts @@ -0,0 +1,29 @@ +import { ConfigPlugin, JssConfig } from '..' + +/** + * This plugin will set config props based on scjssconfig.json. + * scjssconfig.json may not exist if you've never run `jss setup` (development) + * or are depending on environment variables instead (production). + */ +class ScJssConfigPlugin implements ConfigPlugin { + order = 1 + + async exec(config: JssConfig) { + let scJssConfig + try { + scJssConfig = require('scjssconfig.json') + } catch (e) { + return config + } + + if (!scJssConfig) return config + + return Object.assign({}, config, { + sitecoreApiKey: config.sitecoreApiKey || scJssConfig.sitecore?.apiKey, + sitecoreApiHost: + config.sitecoreApiHost || scJssConfig.sitecore?.layoutServiceHost, + }) + } +} + +export const scjssconfigPlugin = new ScJssConfigPlugin() diff --git a/examples/cms-sitecore-xmcloud/scripts/fetch-graphql-introspection-data.ts b/examples/cms-sitecore-xmcloud/scripts/fetch-graphql-introspection-data.ts new file mode 100644 index 000000000000..2a66c7235271 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/scripts/fetch-graphql-introspection-data.ts @@ -0,0 +1,48 @@ +import { GraphQLRequestClient } from '@sitecore-jss/sitecore-jss-nextjs' +import fs from 'fs' +import { getIntrospectionQuery } from 'graphql' + +// This script load graphql introspection data in order to use graphql code generator and generate typescript types +// The `jss graphql:update` command should be executed when Sitecore templates related to the site are altered. + +let jssConfig + +try { + // eslint-disable-next-line + jssConfig = require('../src/temp/config') +} catch (e) { + console.error( + 'Unable to require JSS config. Ensure `jss setup` has been run, and the app has been started at least once after setup.' + ) + console.error(e) + process.exit(1) +} + +console.log( + `Fetch graphql introspection data from ${jssConfig.graphQLEndpoint}...` +) + +const client = new GraphQLRequestClient(jssConfig.graphQLEndpoint, { + apiKey: jssConfig.sitecoreApiKey, +}) + +client + .request(getIntrospectionQuery()) + .then((result) => { + fs.writeFile( + './src/temp/GraphQLIntrospectionResult.json', + JSON.stringify(result, null, 2), + (err) => { + if (err) { + console.error('Error writing GraphQLIntrospectionResult file', err) + return + } + + console.log('GraphQL Introspection Data successfully fetched!') + } + ) + }) + .catch((e) => { + console.error(e) + process.exit(1) + }) diff --git a/examples/cms-sitecore-xmcloud/scripts/generate-component-factory.ts b/examples/cms-sitecore-xmcloud/scripts/generate-component-factory.ts new file mode 100644 index 000000000000..72d0d411ce6d --- /dev/null +++ b/examples/cms-sitecore-xmcloud/scripts/generate-component-factory.ts @@ -0,0 +1,97 @@ +import fs from 'fs' +import path from 'path' +import generateComponentFactory, { + ComponentFile, + PackageDefinition, +} from './templates/component-factory' +import { getItems, watchItems } from './utils' + +/* + COMPONENT FACTORY GENERATION + Generates the `/src/temp/componentFactory.ts` file, which maps JSS React components + to Sitecore renderings. + + The component factory is a mapping between a string name and a React component instance. + When the Sitecore Layout service returns a layout definition, it returns named components. + This mapping is used to construct the component hierarchy for the layout. + + Generating the componentFactory is optional, and it can be maintained manually if preferred. + + The default convention uses the component's filename (without the extension) as the component + name. For example, the file `/components/ComponentName.ts` would map to component `ComponentName`. + This can be customized in writeComponentFactory(). + + This script supports two modes. In default mode, the component factory file is written once. + In watch mode, the component factory source folder is watched, and componentFactory.ts is + regenerated whenever files are added or deleted. Run in watch mode by passing a `--watch` argument + when calling the script. +*/ + +const componentFactoryPath = path.resolve('src/temp/componentFactory.ts') +const componentRootPath = 'src/components' + +const isWatch = process.argv.some((arg) => arg === '--watch') + +function getComponentList(path: string): (PackageDefinition | ComponentFile)[] { + const components = getItems({ + path, + resolveItem: (path, name) => ({ + path: `${path}/${name}`, + componentName: name, + moduleName: name.replace(/[^\w]+/g, ''), + }), + cb: (name) => console.debug(`Registering JSS component ${name}`), + }) + + return components +} + +/** + * Generates the component factory file and saves it to the filesystem. + * By convention, we expect to find React components under src/components/** (subfolders are + * searched recursively). The filename, with the extension stripped, is used for the component's + * string name (for mapping to Sitecore). The filename, with extension and non-word characters + * stripped, is used to identify the component's JavaScript module definition (for initializing + * new component instances in code). + * Modify this function to use a different convention. + */ +function writeComponentFactory() { + /** + * You can specify components which you want to import from external/internal packages + * in format: + * { + * name: 'package name', + * components: [ + * { + * componentName: 'component name', // component rendering name, + * moduleName: 'module name' // component name to import from the package + * } + * ] + * } + */ + const packages: PackageDefinition[] = [] + const components = getComponentList(componentRootPath) + + components.unshift(...packages) + + const fileContent = generateComponentFactory(components) + console.log(`Writing component factory to ${componentFactoryPath}`) + fs.writeFileSync(componentFactoryPath, fileContent, { + encoding: 'utf8', + }) +} + +/** + * Watches component directory for changes. When files are added or deleted, the component factory + * file (componentFactory.ts) is regenerated. This is used during `jss start` to pick up new or + * removed components at runtime. + */ +function watchComponentFactory() { + console.log( + `Watching for changes to component factory sources in ${componentRootPath}...` + ) + + watchItems(componentRootPath, writeComponentFactory) +} + +;(isWatch ? watchComponentFactory : writeComponentFactory)() diff --git a/examples/cms-sitecore-xmcloud/scripts/generate-config.ts b/examples/cms-sitecore-xmcloud/scripts/generate-config.ts new file mode 100644 index 000000000000..6ecf1f4b3013 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/scripts/generate-config.ts @@ -0,0 +1,63 @@ +import 'dotenv/config' +import fs from 'fs' +import path from 'path' +import { constantCase } from 'constant-case' +import { JssConfig, jssConfigFactory } from './config' + +/* + CONFIG GENERATION + Generates the /src/temp/config.js file which contains runtime configuration + that the app can import and use. +*/ + +const defaultConfig: JssConfig = { + sitecoreApiKey: process.env[`${constantCase('sitecoreApiKey')}`], + sitecoreApiHost: process.env[`${constantCase('sitecoreApiHost')}`], + jssAppName: process.env[`${constantCase('jssAppName')}`], + graphQLEndpointPath: process.env[`${constantCase('graphQLEndpointPath')}`], + defaultLanguage: process.env[`${constantCase('defaultLanguage')}`], + graphQLEndpoint: process.env[`${constantCase('graphQLEndpoint')}`], +} + +/** + * Writes the config object to disk with support for environment variables. + * @param {JssConfig} config JSS configuration to write. + */ +function writeConfig(config: JssConfig): void { + let configText = `/* eslint-disable */ +// Do not edit this file, it is auto-generated at build time! +// See scripts/bootstrap.ts to modify the generation of this file. +const config = {};\n` + + // Set configuration values, allowing override with environment variables + Object.keys(config).forEach((prop) => { + configText += `config.${prop} = process.env.${constantCase(prop)} || '${ + config[prop] + }',\n` + }) + configText += `module.exports = config;` + + const configPath = path.resolve('src/temp/config.js') + console.log(`Writing runtime config to ${configPath}`) + fs.writeFileSync(configPath, configText, { encoding: 'utf8' }) +} + +/** + * Generates the JSS config based on config plugins (under ./config/plugins) + * and then writes the config to disk. + * @param {JssConfig} defaultConfig Default configuration. + */ +function generateConfig(defaultConfig: JssConfig): void { + jssConfigFactory + .create(defaultConfig) + .then((config) => { + writeConfig(config) + }) + .catch((e) => { + console.error('Error generating config') + console.error(e) + process.exit(1) + }) +} + +generateConfig(defaultConfig) diff --git a/examples/cms-sitecore-xmcloud/scripts/generate-plugins.ts b/examples/cms-sitecore-xmcloud/scripts/generate-plugins.ts new file mode 100644 index 000000000000..2ec845056909 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/scripts/generate-plugins.ts @@ -0,0 +1,138 @@ +import fs from 'fs' +import path from 'path' +import { getItems } from './utils' + +/* + PLUGINS GENERATION + NOTE: pluginName: the name of the plugin in the src/lib folder + Generates the `/src/temp/{pluginName}-plugins.ts` file, which exports list of plugins + + Generating the plugins is optional, and it can be maintained manually if preferred. + + The default convention uses the plugin's filename (without the extension) as the first part of the component + name. For example, the file `/lib/page-props-factory/plugins/exampleName.ts` would map to plugin `exampleNamePlugin`. + This can be customized in writePlugins(). +*/ + +enum ModuleType { + CJS, + ESM, +} + +interface PluginDefinition { + listPath: string + rootPath: string + moduleType: ModuleType +} + +interface PluginFile { + path: string + name: string +} + +const pluginDefinitions = [ + { + listPath: 'scripts/temp/config-plugins.ts', + rootPath: 'scripts/config/plugins', + moduleType: ModuleType.ESM, + }, + { + listPath: 'src/temp/sitemap-fetcher-plugins.ts', + rootPath: 'src/lib/sitemap-fetcher/plugins', + moduleType: ModuleType.ESM, + }, + { + listPath: 'src/temp/middleware-plugins.ts', + rootPath: 'src/lib/middleware/plugins', + moduleType: ModuleType.ESM, + }, + { + listPath: 'src/temp/page-props-factory-plugins.ts', + rootPath: 'src/lib/page-props-factory/plugins', + moduleType: ModuleType.ESM, + }, + { + listPath: 'src/temp/next-config-plugins.js', + rootPath: 'src/lib/next-config/plugins', + moduleType: ModuleType.CJS, + }, + { + listPath: 'src/temp/extract-path-plugins.ts', + rootPath: 'src/lib/extract-path/plugins', + moduleType: ModuleType.ESM, + }, + { + listPath: 'src/temp/site-resolver-plugins.ts', + rootPath: 'src/lib/site-resolver/plugins', + moduleType: ModuleType.ESM, + }, +] + +function getPluginList(path: string, pluginName: string): PluginFile[] { + const plugins = getItems({ + path, + resolveItem: (path, name) => ({ + path: `${path}/${name}`, + name: `${name.replace(/-./g, (x) => x[1].toUpperCase())}Plugin`, + }), + cb: (name) => console.debug(`Registering ${pluginName} plugin ${name}`), + }) + + return plugins +} + +/** + * Generates the plugins file and saves it to the filesystem. + * By convention, we expect to find plugins under src/lib/{pluginName}/plugins/** (subfolders are + * searched recursively). The filename, with extension and non-word characters + * stripped, is used to identify the plugin's JavaScript module definition (for adding + * new plugin to the factory). + * Modify this function to use a different convention. + */ +function writePlugins( + listPath: string, + rootPath: string, + moduleType: ModuleType +) { + const segments = rootPath.split('/') + const pluginName = segments[segments.length - 2] + const plugins = getPluginList(rootPath, pluginName) + let fileContent = '' + + fileContent = plugins + .map((plugin) => { + return moduleType === ModuleType.CJS + ? `exports.${plugin.name} = require('${plugin.path.replace( + 'src/', + '../' + )}');` + : `export { ${plugin.name} } from '${plugin.path}';` + }) + .join('\r\n') + .concat('\r\n') + + if (!plugins.length) { + fileContent = + moduleType === ModuleType.CJS + ? 'module.exports = {};\r\n' + : 'export {};\r\n' + } + + const filePath = path.resolve(listPath) + console.log(`Writing ${pluginName} plugins to ${filePath}`) + fs.writeFileSync(filePath, fileContent, { + encoding: 'utf8', + }) +} + +function run(definitions: PluginDefinition[]) { + definitions.forEach((definition) => { + writePlugins( + definition.listPath, + definition.rootPath, + definition.moduleType + ) + }) +} + +run(pluginDefinitions) diff --git a/examples/cms-sitecore-xmcloud/scripts/scaffold-component.ts b/examples/cms-sitecore-xmcloud/scripts/scaffold-component.ts new file mode 100644 index 000000000000..32d2f705facf --- /dev/null +++ b/examples/cms-sitecore-xmcloud/scripts/scaffold-component.ts @@ -0,0 +1,95 @@ +/* + Component Scaffolding Script + This is a script that enables scaffolding a new JSS component using `jss scaffold `. + The default convention is that component names must start with a capital letter, and can contain + letters, number, underscores, or dashes. + + If the parameter includes a path, it must be relative to the src/components folder. + For example, `jss scaffold search/SearchBox` will create a component called `SearchBox` in + `src/components/search/SearchBox.tsx`. Specifying a relative path is optional, and just providing + the name is ok. + + Edit this script if you wish to use your own conventions for component storage in your JSS app. +*/ + +/* eslint-disable no-throw-literal,no-console */ + +import fs from 'fs' +import path from 'path' +import chalk from 'chalk' +import generateComponentSrc from './templates/component-src' + +const componentRootPath = 'src/components' + +// Matches component names that start with a capital letter, and contain only letters, number, +// underscores, or dashes. Optionally, the component name can be preceded by a relative path +const nameParamFormat = new RegExp(/^((?:[\w-]+\/)*)([A-Z][\w-]+)$/) +const componentArg = process.argv[2] + +if (!componentArg) { + throw 'Component name was not passed. Usage: jss scaffold ' +} + +const regExResult = nameParamFormat.exec(componentArg) + +if (regExResult === null) { + throw `Component name should start with an uppercase letter and contain only letters, numbers, +dashes, or underscores. If specifying a path, it must be relative to src/components` +} + +const componentPath = regExResult[1] +const componentName = regExResult[2] +const filename = `${componentName}.tsx` + +/** + * Force to use `crlf` line endings, we are using `crlf` across the project. + * Replace: `lf` (\n), `cr` (\r) + * @param {string} content + */ +function editLineEndings(content: string) { + return content.replace(/\r|\n/gm, '\r\n') +} + +/** + * Creates a file relative to the specified path if the file doesn't exist. Creates directories as needed. + * @param {string} rootPath - the root path + * @param {string} fileContent - the file content + * @param {string} filename - the filename + * @returns the new file's filepath + */ +function scaffoldFile( + rootPath: string, + fileContent: string, + filename: string +): string | null { + const outputDir = path.join(rootPath, componentPath) + const outputFile = path.join(outputDir, filename) + + if (fs.existsSync(outputFile)) { + console.log(chalk.red(`Skipping creating ${outputFile}; already exists.`)) + return null + } + + fs.mkdirSync(outputDir, { recursive: true }) + fs.writeFileSync(outputFile, editLineEndings(fileContent), 'utf8') + console.log(chalk.green(`File ${outputFile} has been scaffolded.`)) + return outputFile +} + +const componentOutputPath = scaffoldFile( + componentRootPath, + generateComponentSrc(componentName), + filename +) + +console.log( + chalk.green(` +Scaffolding of ${componentName} complete. +Next steps:`) +) + +if (componentOutputPath) { + console.log( + `* Implement the React component in ${chalk.green(componentOutputPath)}` + ) +} diff --git a/examples/cms-sitecore-xmcloud/scripts/temp/.npmignore b/examples/cms-sitecore-xmcloud/scripts/temp/.npmignore new file mode 100644 index 000000000000..d6b7ef32c847 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/scripts/temp/.npmignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/examples/cms-sitecore-xmcloud/scripts/temp/config-plugins.ts b/examples/cms-sitecore-xmcloud/scripts/temp/config-plugins.ts new file mode 100644 index 000000000000..aacc0009fad8 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/scripts/temp/config-plugins.ts @@ -0,0 +1,4 @@ +export { computedPlugin } from 'scripts/config/plugins/computed' +export { fallbackPlugin } from 'scripts/config/plugins/fallback' +export { packageJsonPlugin } from 'scripts/config/plugins/package-json' +export { scjssconfigPlugin } from 'scripts/config/plugins/scjssconfig' diff --git a/examples/cms-sitecore-xmcloud/scripts/templates/component-factory.ts b/examples/cms-sitecore-xmcloud/scripts/templates/component-factory.ts new file mode 100644 index 000000000000..63cc562fc9e0 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/scripts/templates/component-factory.ts @@ -0,0 +1,142 @@ +/** + * Describes a file that represents a component definition + */ +export interface ComponentFile { + path: string + moduleName: string + componentName: string +} + +export interface PackageDefinition { + name: string + components: { + moduleName: string + componentName: string + }[] +} + +const isLazyLoadingModule = (componentPath: string) => + componentPath.includes('.dynamic') + +const removeDynamicModuleNameEnding = (moduleName: string) => + moduleName.replace(/\.?dynamic$/i, '') + +/** + * Generates the contents of the component factory file using a predefined string template. + * @param components - the list of component files to include + * @returns component factory file contents + */ +function generateComponentFactory( + components: (PackageDefinition | ComponentFile)[] +): string { + const componentFiles = components.filter( + (component) => (component as ComponentFile).path + ) as ComponentFile[] + const packages = components.filter( + (component) => (component as PackageDefinition).components + ) as PackageDefinition[] + + const hasLazyModules = componentFiles.find((component) => + isLazyLoadingModule(component.path) + ) + + return `/* eslint-disable */ +// Do not edit this file, it is auto-generated at build time! +// See scripts/generate-component-factory.ts to modify the generation of this file. + +${hasLazyModules ? "import dynamic from 'next/dynamic'" : ''} + +${packages.map((pkg) => { + const list = pkg.components.map((c) => c.moduleName).join(', ') + + return `import { ${list} } from '${pkg.name}'` +})} +${componentFiles + .map((component) => { + if (isLazyLoadingModule(component.path)) { + const moduleName = removeDynamicModuleNameEnding(component.moduleName) + return `const ${moduleName} = { + module: () => import('${component.path}'), + element: (isEditing?: boolean) => isEditing ? require('${component.path}')?.default : dynamic(${moduleName}.module) +}` + } + + return `import * as ${component.moduleName} from '${component.path}';` + }) + .join('\n')} + +const components = new Map(); +${packages.map((p) => + p.components.map( + (component) => + `components.set('${component.componentName}', ${component.moduleName})` + ) +)} +${componentFiles + .map( + (component) => + `components.set('${ + isLazyLoadingModule(component.path) + ? removeDynamicModuleNameEnding(component.componentName) + : component.componentName + }', ${ + isLazyLoadingModule(component.path) + ? removeDynamicModuleNameEnding(component.moduleName) + : component.moduleName + });` + ) + .join('\n')} + +// Next.js 'dynamic' import and JavaScript 'dynamic' import are different. +// Next.js 'dynamic(...)' returns common 'React.ComponentType' while +// 'import('...')' returns 'Promise' that will resolve module. +// componentModule uses 'import(...)' because primary usage of it to get not only 'React Component' (default export) but all named exports. +// See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#dynamic_imports +// componentFactory uses 'dynamic(...)' because primary usage of it to render 'React Component' (default export). +// See https://nextjs.org/docs/advanced-features/dynamic-import +// At the end you will have single preloaded script for each lazy loading module. +// Editing mode doesn't work well with dynamic components in nextjs: dynamic components are not displayed without refresh after a rendering is added. +// This happens beacuse Sitecore editors simply insert updated HTML generated on server side. This conflicts with nextjs dynamic logic as no HTML gets rendered for dynamic component +// So we use require() to obtain dynamic components in editing mode while preserving dynamic logic for non-editing scenarios +// As we need to be able to seamlessly work with dynamic components in both editing and normal modes, different componentFactory functions will be passed to app + +export function componentModule(componentName: string) { + const component = components.get(componentName); + + // check that component is lazy loading module + if (!component?.default && component?.module) { + // return js dynamic import + return component.module(); + } + + return component; +} + +function baseComponentFactory(componentName: string, exportName?: string, isEditing?: boolean) { + const DEFAULT_EXPORT_NAME = 'Default'; + const component = components.get(componentName); + + // check that component should be dynamically imported + if (component?.element) { + // return next.js dynamic import + return component.element(isEditing); + } + + if (exportName && exportName !== DEFAULT_EXPORT_NAME) { + return component[exportName]; + } + + return component?.Default || component?.default || component; +} + +export function componentFactory(componentName: string, exportName?: string) { + return baseComponentFactory(componentName, exportName, false); +} + +export function editingComponentFactory(componentName: string, exportName?: string) { + return baseComponentFactory(componentName, exportName, true); +} +` +} + +export default generateComponentFactory diff --git a/examples/cms-sitecore-xmcloud/scripts/templates/component-src.ts b/examples/cms-sitecore-xmcloud/scripts/templates/component-src.ts new file mode 100644 index 000000000000..9adae12a29c1 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/scripts/templates/component-src.ts @@ -0,0 +1,27 @@ +/** + * Generates React boilerplate for a component under `src/components` + * @param componentName - the component name + * @returns component src boilerplate as a string + */ +function generateComponentSrc(componentName: string): string { + return `import { Text, Field, withDatasourceCheck } from '@sitecore-jss/sitecore-jss-nextjs'; +import { ComponentProps } from 'lib/component-props'; + +type ${componentName}Props = ComponentProps & { + fields: { + heading: Field; + }; +}; + +const ${componentName} = (props: ${componentName}Props): JSX.Element => ( +
+

${componentName} Component

+ +
+); + +export default withDatasourceCheck()<${componentName}Props>(${componentName}); +` +} + +export default generateComponentSrc diff --git a/examples/cms-sitecore-xmcloud/scripts/utils.ts b/examples/cms-sitecore-xmcloud/scripts/utils.ts new file mode 100644 index 000000000000..ab9e496f5b67 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/scripts/utils.ts @@ -0,0 +1,64 @@ +import fs from 'fs' +import chokidar from 'chokidar' + +/** + * Run watch mode, watching on @var rootPath + */ +export function watchItems(rootPath: string, cb: () => void): void { + chokidar + .watch(rootPath, { ignoreInitial: true, awaitWriteFinish: true }) + .on('add', cb) + .on('unlink', cb) +} + +/** + * Using @var path find all files recursively and generate output using @var resolveItem by calling it for each file + * @param path plugins path + * @param resolveItem will resolve item in required data format + * @param cb will be called when new item is found + * @param fileFormat Matches specific files + * @returns {Item[]} items + */ +export function getItems(settings: { + path: string + resolveItem: (path: string, name: string) => Item + cb?: (name: string) => void + fileFormat?: RegExp +}): Item[] { + const { + path, + resolveItem, + cb, + fileFormat = new RegExp(/(.+)(? { + if (item.isDirectory()) { + folders.push(item) + } + + if (fileFormat.test(item.name)) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const name = item.name.match(fileFormat)![1] + items.push(resolveItem(path, name)) + cb && cb(name) + } + }) + + for (const folder of folders) { + items.push( + ...getItems({ + path: `${path}/${folder.name}`, + resolveItem, + cb, + fileFormat, + }) + ) + } + + return items +} diff --git a/examples/cms-sitecore-xmcloud/sitecore/config/xmcloud-nextjs-starter.config b/examples/cms-sitecore-xmcloud/sitecore/config/xmcloud-nextjs-starter.config new file mode 100644 index 000000000000..e0a7e02d4a43 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/sitecore/config/xmcloud-nextjs-starter.config @@ -0,0 +1,165 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + mw=100,mh=50 + + + mw=16 + mw=32 + mw=48 + mw=64 + mw=96 + mw=128 + mw=256 + mw=384 + mw=640 + mw=750 + mw=828 + mw=1080 + mw=1200 + mw=1920 + mw=2048 + mw=3840 + + + + + + + + + + false + + + + + + + false + + + + + + + diff --git a/examples/cms-sitecore-xmcloud/src/Layout.tsx b/examples/cms-sitecore-xmcloud/src/Layout.tsx new file mode 100644 index 000000000000..42f6d7e765fc --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/Layout.tsx @@ -0,0 +1,63 @@ +/** + * This Layout is needed for Starter Kit. + */ +import React from 'react' +import Head from 'next/head' +import { + Placeholder, + getPublicUrl, + LayoutServiceData, + Field, +} from '@sitecore-jss/sitecore-jss-nextjs' +import Scripts from 'src/Scripts' + +// Prefix public assets with a public URL to enable compatibility with Sitecore Experience Editor. +// If you're not supporting the Experience Editor, you can remove this. +const publicUrl = getPublicUrl() + +interface LayoutProps { + layoutData: LayoutServiceData +} + +interface RouteFields { + [key: string]: unknown + Title?: Field +} + +const Layout = ({ layoutData }: LayoutProps): JSX.Element => { + const { route } = layoutData.sitecore + const fields = route?.fields as RouteFields + const isPageEditing = layoutData.sitecore.context.pageEditing + const mainClassPageEditing = isPageEditing ? 'editing-mode' : 'prod-mode' + + return ( + <> + + + {fields?.Title?.value?.toString() || 'Page'} + + + + {/* root placeholder for the app, which we add components to using route data */} +
+
+ +
+
+
+ {route && } +
+
+
+ +
+
+ + ) +} + +export default Layout diff --git a/examples/cms-sitecore-xmcloud/src/Navigation.tsx b/examples/cms-sitecore-xmcloud/src/Navigation.tsx new file mode 100644 index 000000000000..c1e7a1446f61 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/Navigation.tsx @@ -0,0 +1,27 @@ +import { getPublicUrl } from '@sitecore-jss/sitecore-jss-nextjs' + +// Prefix public assets with a public URL to enable compatibility with Sitecore editors. +// If you're not supporting Sitecore editors, you can remove this. +const publicUrl = getPublicUrl() + +const Navigation = (): JSX.Element => ( +
+ +
+) + +export default Navigation diff --git a/examples/cms-sitecore-xmcloud/src/NotFound.tsx b/examples/cms-sitecore-xmcloud/src/NotFound.tsx new file mode 100644 index 000000000000..e4e59d16b6e3 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/NotFound.tsx @@ -0,0 +1,19 @@ +import Head from 'next/head' + +/** + * Rendered in case if we have 404 error + */ +const NotFound = (): JSX.Element => ( + <> + + 404: NotFound + +
+

Page not found

+

This page does not exist.

+ Go to the Home page +
+ +) + +export default NotFound diff --git a/examples/cms-sitecore-xmcloud/src/Scripts.tsx b/examples/cms-sitecore-xmcloud/src/Scripts.tsx new file mode 100644 index 000000000000..c57b322012fd --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/Scripts.tsx @@ -0,0 +1,5 @@ +const Scripts = (): JSX.Element | null => { + return null +} + +export default Scripts diff --git a/examples/cms-sitecore-xmcloud/src/assets/app.css b/examples/cms-sitecore-xmcloud/src/assets/app.css new file mode 100644 index 000000000000..489cdc72b0b2 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/app.css @@ -0,0 +1,39 @@ +a[target='_blank']:after { + content: '\1F5D7'; +} + +/* + Hides Sitecore editor markup, + if you run the app in connected mode while the Sitecore cookies + are set to edit mode. +*/ +.scChromeData, +.scpm { + display: none !important; +} + +/* + Styles for default JSS error components +*/ +.sc-jss-editing-error, +.sc-jss-placeholder-error { + padding: 1em; + background-color: lightyellow; +} + +/* + Style for default content block +*/ +.contentTitle { + font-size: 3.5rem; + font-weight: 300; + line-height: 1.2; +} + +a { + text-decoration: none; +} + +a:hover { + text-decoration: underline; +} diff --git a/examples/cms-sitecore-xmcloud/src/assets/basic/_component.scss b/examples/cms-sitecore-xmcloud/src/assets/basic/_component.scss new file mode 100644 index 000000000000..6081809a40de --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/basic/_component.scss @@ -0,0 +1,18 @@ +.component-content { + @include respond-to(mobile-large) { + .row { + padding: 0; + margin: 0; + } + } +} +@include respond-to(mobile-large) { + .row { + margin: 0; + padding: 0; + > * { + padding: 0; + margin: 0; + } + } +} diff --git a/examples/cms-sitecore-xmcloud/src/assets/basic/_container.scss b/examples/cms-sitecore-xmcloud/src/assets/basic/_container.scss new file mode 100644 index 000000000000..8644503433da --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/basic/_container.scss @@ -0,0 +1,79 @@ +@import '@sass/abstracts/mixins'; +@import 'variables'; + +body { + font-family: Roboto; + color: $main-color; +} + +@media (min-width: 1400px) { + .container { + max-width: 1250px; + } +} + +/**** START STYLE MAX HEIGHT ****/ +.prod-mode { + display: flex; + flex-direction: column; + height: 100vh; + + main { + flex: 1 0 auto; + } + + footer { + flex-shrink: 0; + } +} +/**** END STYLE MAX HEIGHT ****/ + +main { + .main-header { + margin-top: 55px; + margin-bottom: 30px; + + @include respond-to(mobile-large) { + margin-bottom: 0; + } + + h4 { + font-size: $text-size-50; + font-weight: 500; + line-height: 70px; + } + } + + .main-news-header { + margin-top: 60px; + + h2 { + font-size: $text-size-36; + line-height: 43px; + } + } + + @include respond-to(mobile-large) { + padding-top: 0; + margin-bottom: 0; + + .main-header { + margin-top: 10px; + + h4 { + font-size: $text-size-24; + line-height: 32px; + } + } + + .main-news-header { + padding-bottom: 35px; + + h2 { + margin-top: 0; + font-size: $text-size-18; + line-height: 22px; + } + } + } +} diff --git a/examples/cms-sitecore-xmcloud/src/assets/basic/_fonts.scss b/examples/cms-sitecore-xmcloud/src/assets/basic/_fonts.scss new file mode 100644 index 000000000000..d383a9572f68 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/basic/_fonts.scss @@ -0,0 +1 @@ +@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap'); diff --git a/examples/cms-sitecore-xmcloud/src/assets/basic/_footer.scss b/examples/cms-sitecore-xmcloud/src/assets/basic/_footer.scss new file mode 100644 index 000000000000..bc97d5ce8507 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/basic/_footer.scss @@ -0,0 +1,32 @@ +@import '@sass/abstracts/mixins'; +@import 'variables'; + +footer { + @include respond-to(mobile-large) { + padding-top: 0; + } + .container-dark-background { + background-color: #262626; + } + + .contacts { + font-size: $text-size-14; + + a { + color: $text-white; + } + + p, + span { + color: $text-white; + } + } + + .indent-inner { + padding: 65px; + + @include respond-to(mobile-large) { + padding: 40px 0; + } + } +} diff --git a/examples/cms-sitecore-xmcloud/src/assets/basic/_header.scss b/examples/cms-sitecore-xmcloud/src/assets/basic/_header.scss new file mode 100644 index 000000000000..e5810fee4a45 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/basic/_header.scss @@ -0,0 +1,49 @@ +@import '@sass/abstracts/vars/colors'; +@import '@sass/abstracts/mixins'; +@import 'variables'; + +.prod-mode { + #header { + display: flex; + + @include respond-to(mobile-large) { + padding-bottom: 0; + flex-direction: column-reverse; + } + } +} + +header { + #header { + .bs-title { + padding-left: 50px; + + h1 { + font-size: $text-size-24; + font-weight: 600; + line-height: 18px; + padding-top: 40px; + margin: 0; + } + } + + @include respond-to(mobile-large) { + padding-bottom: 0; + padding-top: 0; + flex-direction: column-reverse; + + .bs-title { + padding-left: 0; + text-align: center; + margin-top: -5px; + + h1 { + font-weight: 500; + line-height: 29px; + padding-top: 0; + margin-bottom: 10px; + } + } + } + } +} diff --git a/examples/cms-sitecore-xmcloud/src/assets/basic/_navigation.scss b/examples/cms-sitecore-xmcloud/src/assets/basic/_navigation.scss new file mode 100644 index 000000000000..43e7ef89ff93 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/basic/_navigation.scss @@ -0,0 +1,152 @@ +@import '@sass/abstracts/vars'; +@import 'variables'; + +$hamburger-width: 28px; +$hamburger-height: 18px; +$border-size: 6px; +$hamburger-margin: 18px; + +.navigation { + .menu-mobile-navigate-wrapper { + width: 100%; + } +} + +.navigation.navigation-horizontal { + width: 100%; + padding-right: 70px; + margin-top: -15px; + + .menu-mobile-navigate { + display: none; + } + + @include respond-to(mobile-large) { + padding-right: 0; + } + + .component-content { + display: inline-block; + + @include respond-to(mobile-large) { + display: none; + } + + ul.clearfix { + list-style: none; + } + .level0 { + display: flex; + } + + .level0, + .level1 { + float: left; + margin-left: 30px; + + > .navigation-title { + > a { + border-width: 0; + font-size: $text-size-14; + font-weight: 400; + } + } + } + } + + @include respond-to(mobile-large) { + .component-content { + display: none; + position: fixed; + top: 0; + bottom: 0; + right: 0; + left: 0; + background-color: $bg-black-active; + z-index: 1; + + * { + text-align: center !important; + } + + nav { + padding-top: 110px; + + .level0 { + display: block; + } + } + + ul { + margin: 0; + padding: 0; + } + + .level0, + .level1 { + float: unset; + margin-left: 0; + > .navigation-title { + > a { + font-size: $text-size-30; + line-height: 90px; + > span { + color: $text-white; + } + } + } + } + } + + .menu-mobile-navigate-wrapper { + .menu-mobile-navigate { + display: inline-block; + z-index: 2; + position: absolute; + right: $hamburger-margin; + top: $hamburger-margin; + height: $hamburger-width; + width: $hamburger-width; + opacity: 0; + + &:checked ~ { + .menu-humburger { + &::before { + content: '\00d7'; + color: $text-basic-active; + font-size: $text-size-48; + position: fixed; + top: 0; + right: 0; + margin-top: calc($hamburger-margin + ($hamburger-margin / 2)); + margin-right: $hamburger-margin; + line-height: 0; + border: 0; + } + } + + .component-content { + position: fixed; + display: inline-block; + } + } + } + + .menu-humburger { + &::before { + content: ''; + width: $hamburger-width; + height: $hamburger-height; + margin-right: $hamburger-margin; + margin-top: calc($hamburger-margin + ($hamburger-margin / 2)) + 8px; + display: block; + border-top: 6px solid $bg-black; + border-bottom: 6px solid $bg-black; + float: right; + z-index: 99; + cursor: pointer; + } + } + } + } +} diff --git a/examples/cms-sitecore-xmcloud/src/assets/basic/_promo.scss b/examples/cms-sitecore-xmcloud/src/assets/basic/_promo.scss new file mode 100644 index 000000000000..255b26c9b3d9 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/basic/_promo.scss @@ -0,0 +1,58 @@ +@import '@sass/abstracts/vars'; +@import '@sass/abstracts/mixins'; +/**PROMO**/ + +.promo { + &.main-promo-no-border { + padding-left: 0; + padding-right: 0; + padding-bottom: 0; + margin-bottom: 80px; + + @include respond-to(mobile-large) { + margin-bottom: 0; + } + + > .component-content { + border: 0; + max-width: 583px; + margin-right: 50px; + + > div { + padding-bottom: 0; + } + + @include respond-to(mobile-large) { + max-width: 100%; + margin-right: 0; + } + + .promo-text { + .field-promotext { + font-size: 14px; + + h3 { + font-size: $text-size-18; + margin: 15px 0; + + @include respond-to(mobile-large) { + margin: 10px 0; + } + } + p { + margin: 10px 0; + } + } + } + @include respond-to(mobile-large) { + > div { + padding: 0; + } + + .promo-text { + padding: 15px 30px 5px 30px; + } + } + } + } +} diff --git a/examples/cms-sitecore-xmcloud/src/assets/basic/_rich-text.scss b/examples/cms-sitecore-xmcloud/src/assets/basic/_rich-text.scss new file mode 100644 index 000000000000..ae6878e5a0ce --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/basic/_rich-text.scss @@ -0,0 +1,11 @@ +@import '@sass/abstracts/vars'; +@import 'variables'; + +.rich-text { + font-size: $text-size-16; + + @include respond-to(mobile-large) { + padding: 0 30px 10px 30px; + font-size: $text-size-14; + } +} diff --git a/examples/cms-sitecore-xmcloud/src/assets/basic/_variables.scss b/examples/cms-sitecore-xmcloud/src/assets/basic/_variables.scss new file mode 100644 index 000000000000..ad4c5f8bc79f --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/basic/_variables.scss @@ -0,0 +1,10 @@ +$text-size-14: 14px; +$text-size-16: 16px; +$text-size-18: 18px; +$text-size-24: 24px; +$text-size-30: 30px; +$text-size-36: 36px; +$text-size-48: 48px; +$text-size-50: 50px; + +$main-color: #27272a; diff --git a/examples/cms-sitecore-xmcloud/src/assets/basic/main.scss b/examples/cms-sitecore-xmcloud/src/assets/basic/main.scss new file mode 100644 index 000000000000..2ce2e49be80e --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/basic/main.scss @@ -0,0 +1,8 @@ +@import 'fonts'; +@import 'container'; +@import 'header'; +@import 'promo'; +@import 'navigation'; +@import 'component'; +@import 'rich-text'; +@import 'footer'; diff --git a/examples/cms-sitecore-xmcloud/src/assets/main.scss b/examples/cms-sitecore-xmcloud/src/assets/main.scss new file mode 100644 index 000000000000..addfe5ca592e --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/main.scss @@ -0,0 +1,5 @@ +@import 'basic/fonts'; +@import 'font-awesome/scss/font-awesome'; +@import 'bootstrap/scss/bootstrap'; +@import 'sass/main.scss'; +@import 'basic/main.scss'; diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/_app.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/_app.scss new file mode 100644 index 000000000000..10ba4ad39b1c --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/_app.scss @@ -0,0 +1,105 @@ +html { + font-size: 62.5%; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + margin-bottom: 0px; + height: auto !important; +} + +body { + @include opensans-font-stack(); + position: relative; + overflow: auto; + color: $text-basic; + font-size: 16px; + line-height: 1.5; + background: $page-bg; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); + height: auto !important; + &.on-page-editor { + background: $page-bg-editor !important; + } +} + +a { + color: $text-basic; + cursor: pointer; + font-size: 0.85em; + &:hover { + color: $text-basic-active; + } +} + +h1, +h2, +h3, +h4, +h5, +h6 { + font-weight: bold; +} + +h1 { + font-size: 2em; +} + +h2 { + font-size: 1.5em; +} + +h3 { + font-size: 1.2em; +} + +h4 { + font-size: 1em; +} + +h5 { + font-size: 0.83em; +} + +h6 { + font-size: 0.67em; +} + +ul li { + list-style-type: none; +} + +.xa-variable { + border: 0px; + padding: 1px; + margin: 0px; + background-color: #ebebe4; + color: #545454; + user-select: none; + pointer-events: none; +} + +//Navigation Bar fix +#breadcrumbMenuSubcontrol_context_menu * { + box-sizing: initial; +} + +//End navigation bar fix +.menu-mobile-navigate { + display: none; +} + +#header, +#content, +#footer, +.sc-jss-empty-placeholder { + width: 100%; + display: flex; + flex-wrap: wrap; +} + +.sc-jss-placeholder-error { + background: #ff0000; + outline: 5px solid #e36565; + padding: 10px; + color: #fff; + max-width: 500px; +} diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/abstracts/_functions.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/abstracts/_functions.scss new file mode 100644 index 000000000000..fdef2da81ddb --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/abstracts/_functions.scss @@ -0,0 +1,7 @@ +@function headings($from: 1, $to: 6) { + @if $from==$to { + @return 'h#{$from}'; + } @else { + @return 'h#{$from},' + headings($from + 1, $to); + } +} diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/abstracts/_mixins.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/abstracts/_mixins.scss new file mode 100644 index 000000000000..b6be54ed0846 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/abstracts/_mixins.scss @@ -0,0 +1,121 @@ +@import 'vars'; + +/* breakpoints */ + +$break-desktop: 1100px; +$break-mobile: 380px; +$break-mobile-horizontal: 640px; +$break-mobile-large: 992px; +@mixin wrapper() { + max-width: 960px; + margin: 0 auto !important; +} +@mixin clearfix { + &:after { + content: ''; + display: table; + clear: both; + } +} +@mixin headings($from: 1, $to: 6) { + @for $i from $from through $to { + h#{$i} { + @content; + } + } +} +@mixin font-size($sizeValue: 1.6) { + font-size: ($sizeValue * 10) + px; + font-size: $sizeValue + rem; +} +@mixin proxima-font($weight: semibold) { + @if ($weight==semibold) { + font-family: 'ProximaNova-Semibold', arial, helvetica, sans-serif; + } @else if($weight==light) { + font-family: 'ProximaNova-Light', arial, helvetica, sans-serif; + } +} +@mixin opensans-font-stack() { + font-family: 'Open Sans', Helvetica, Verdana, Tahoma, sans-serif; +} +@mixin loading-gif() { + background-image: url(data:image/svg+xml;charset=utf-8;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzMiAzMiIgd2lkdGg9IjMyIiBoZWlnaHQ9IjMyIiBmaWxsPSJibGFjayI+DQogIDxwYXRoICBvcGFjaXR5PSIuMjUiIGQ9Ik0xNiAwIEExNiAxNiAwIDAgMCAxNiAzMiBBMTYgMTYgMCAwIDAgMTYgMCBNMTYgNCBBMTIgMTIgMCAwIDEgMTYgMjggQTEyIDEyIDAgMCAxIDE2IDQiLz4NCiAgPHBhdGggZmlsbD0nIzFhODBiNicgZD0iTTE2IDAgQTE2IDE2IDAgMCAxIDMyIDE2IEwyOCAxNiBBMTIgMTIgMCAwIDAgMTYgNHoiPg0KICAgIDxhbmltYXRlVHJhbnNmb3JtIGF0dHJpYnV0ZU5hbWU9InRyYW5zZm9ybSIgdHlwZT0icm90YXRlIiBmcm9tPSIwIDE2IDE2IiB0bz0iMzYwIDE2IDE2IiBkdXI9IjAuOHMiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiAvPg0KICA8L3BhdGg+DQo8L3N2Zz4NCg==); + background-position: center center; + background-repeat: no-repeat; +} +@mixin respond-to($media) { + @if $media==mobile { + @media only screen and (max-width: $break-mobile) { + @content; + } + } @else if $media==mobile-horizontal { + @media only screen and (max-width: $break-mobile-horizontal - 1) { + @content; + } + } @else if $media==mobile-large { + @media only screen and (max-width: $break-mobile-large) { + @content; + } + } @else if $media==tablet { + @media only screen and (min-width: $break-mobile + 1) and (max-width: $break-desktop - 1) { + @content; + } + } @else if $media==all-mobile { + @media only screen and (max-width: $break-desktop - 1) { + @content; + } + } @else if $media==desktop { + @media only screen and (min-width: $break-desktop) { + @content; + } + } +} +@mixin border-basic( + $position: all, + $border-color: $border-gray, + $border-width: 1px +) { + @if ($position!=all) { + border-#{$position}-width: $border-width; + border-#{$position}-style: solid; + border-#{$position}-color: $border-color; + } @else { + border-width: $border-width; + border-style: solid; + border-color: $border-color; + } +} +@mixin fixed-bg($pos, $min-height: 240px) { + background-position: $pos; + background-attachment: fixed; + min-height: $min-height; +} +@mixin linear-gradient($direction, $color-stops...) { + // Direction has been omitted and happens to be a color-stop + @if is-direction($direction) ==false { + $color-stops: $direction, $color-stops; + $direction: 180deg; + } + background: nth(nth($color-stops, 1), 1); + background: -webkit-linear-gradient( + legacy-direction($direction), + $color-stops + ); + background: linear-gradient($direction, $color-stops); +} +@mixin default-link-button { + @include border-basic(); + @include font-size(1.2); + display: inline-block; + vertical-align: middle; + box-sizing: border-box; + margin: 5px 0; + padding: 10px 15px; + text-align: center; + text-decoration: none; + font-weight: bold; + &:hover { + color: $text-basic; + background: $bg-light-gray; + } +} diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/abstracts/_vars.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/abstracts/_vars.scss new file mode 100644 index 000000000000..c3c1d94e358d --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/abstracts/_vars.scss @@ -0,0 +1,3 @@ +@import 'vars/colors'; +@import 'vars/margins'; +@import 'vars/fontSizes'; diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/abstracts/vars/_colors.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/abstracts/vars/_colors.scss new file mode 100644 index 000000000000..c7b73f40c87e --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/abstracts/vars/_colors.scss @@ -0,0 +1,283 @@ +//Backgrounds colors +$bg-transparent: transparent !default; +$bg-basic-color: #ffffff !default; +$bg-basic-color-active: #cccccc !default; +$bg-light-gray: #f7f7f7 !default; +$bg-light-gray-active: #dadada !default; +$bg-blue: #89c6cc; +$bg-blue-active: #15909c !default; +$bg-submenu: #edebeb !default; +$bg-submenu-active: #f6f6f6 !default; +$bg-black: #000; +$bg-black-active: #3d3d3d; +$bg-dark-gray: #262626; +//Text colors +$text-white: #fff !default; +$text-heading-color: #222 !default; +$text-basic: #747474 !default; +$text-basic-active: #878787 !default; +$text-blue: #89c6cc !default; +$text-blue-active: #15909c !default; +$text-submenu-active: #222 !default; +$text-disabled: #aaa !default; +$text-black: #000 !default; +$text-red: #de232f; +$text-gray: #262626; +//Border colors +$border-gray: #d2d2d2 !default; +$border-white: #ffffff !default; +$border-basic-color: #89c6cc !default; +$border-basic-active: #15909c !default; +$border-nav-submenu: #e1e1e1; +//Styles for each component separately +//Accordion +$accordion-header-bg: transparent; +$accordion-header-bg-active: transparent; +$accordion-toggled-bg: transparent; +$accordion-header-border: $border-gray; +//Breadcrumb +$breadcrumb-dropdown-bg: $bg-basic-color; +$breadcrumb-dropdown-bg-active: $bg-blue; +$breadcrumb-dropdown-text-active: $text-white; +$breadcrumb-bg: transparent; +$breadcrumb-color: $text-basic; +$breadcrumb-color-active: $text-blue-active; +//Buttons colors +$btn-green-light: #a0ce4e; +$btn-green: #92be43; +$btn-red-active: #c34e30; +$btn-red: #bc4526; +//Carousel +$carousel-bg: $bg-basic-color; +$carousel-nav: $text-black; +$carousel-nav-active: $text-basic-active; +$carousel-nav-border: $border-basic-color; +//Container component +$container-title-row-bg: $bg-light-gray; +//Event List +$event-list-bg: transparent; +$event-list-item-bg: $bg-basic-color; +$event-list-item-color: $text-basic; +$event-list-title-border: $border-basic-color; +//Calendar +$calendar-bg: $bg-basic-color; +$calendar-header-bg: $bg-basic-color-active; +$calendar-day-color-active: $text-white; +$calendar-title-color: $text-white; +//Feed +$feed-bg: transparent; +$feed-item-bg: transparent; +//Field Editor +$field-editor-bg: transparent; +$field-editor-table-border: $bg-light-gray; +$field-editor-text-header: $text-black; +$field-editor-text-header-active: $text-red; +$field-editor-text: $text-basic; +//File List +$file-list-bg: transparent; +$file-list-item-bg: transparent; +$file-list-title-color: $text-basic; +$file-list-title-border: transparent; +$file-list-item-color: $text-basic; +$file-list-item-color-active: $text-basic-active; +$file-list-item-size: $text-basic; +$file-list-item-borer: $border-basic-color; +$file-list-link-btn-color-active: $text-white; +$file-list-link-btn-color: $text-basic; +$file-list-link-btn-bg: $bg-blue-active; +$file-list-link-btn-bg-active: $bg-blue; +//Flip +$flip-bg: transparent; +$flip-slides-bg: $bg-basic-color; +$flip-slides0-bg: #f6f6f6; +$flip-slides1-bg: $bg-blue-active; +$flip-slides1-color: $text-white; +$flip-border: $border-gray; +//Gallery +$galleria-container-bg: transparent; +$gallery-info-bg: $bg-basic-color; +$gallery-info-border: $border-gray; +$gallery-info-text: $text-basic; +$gallery-nav-active: $text-white; +$gallery-nav: $text-basic; +$gallery-counter-color: $text-white; +//Language selector +$lang-selector-bg: $bg-basic-color; +$lang-selector-border: $border-basic-color; +$lang-selector-item-bg-active: $bg-basic-color; +$lang-selector-item-border: $border-white; +$lang-selector-item-border-active: $border-basic-active; +//Site selector +$site-selector-color: $text-basic; +//Link List +$link-list-bg: transparent; +$link-list-header-border: $border-basic-color; +$link-list-items-bg: transparent; +$link-list-item-bg: transparent; +$link-list-item-color: $text-basic; +$link-list-item-color-active: $text-basic-active; +$link-list-item-border-active: $border-basic-color; +//Login +$login-bg: transparent; +//Logout +$logout-bg: transparent; +$logout-link-text-color: $text-basic; +$logout-link-text-color-active: $text-basic-active; +$logout-link-border: $border-basic-color; +//Map +$map-bg: transparent; +$map-border: none; +//Page List +$page-list-bg: $bg-transparent; +$page-list-item-bg: $bg-transparent; +$page-list-item-title-text: $text-black; +$page-list-item-border: $border-basic-color; +//Pagination +$list-pagination-bg: transparent; +$list-pagination-active-bg: $bg-blue; +$list-pagination-active-color: $text-white; +$list-pagination-active-color: $text-blue; +$list-pagination-active-bg: $bg-submenu-active; +$list-pagination-active-border: $border-basic-active; +//Play list +$play-list-bg: transparent; +$play-list-item-bg: transparent; +$play-list-item-color: $text-basic; +$play-list-item-color-active: $text-white; +$play-list-nav-active: $text-blue; +$play-list-item-bg: $bg-light-gray; +$play-list-item-active-bg: $bg-blue; +$play-list-border: $border-basic-color; +$play-list-title-border: $border-basic-color; +//Promo +$promo-bg: $bg-basic-color; +$promo-bg-hero: rgba(0, 0, 0, 0.5); +$promo-border: $border-gray; +$promo-hero-text-color: $text-white; +$promo-shadow-border: $border-basic-color; +//Rich Text Content +$rich-content-bg: transparent; +$rich-content-color: $text-basic; +$rich-content-border: transparent; +$rich-content-link-color: $text-red; +$rich-content-link-color-active: $text-basic-active; +//Search +$search-filter: $text-basic; +$search-filter-border: $border-basic-color; +//Menu colors +$menu-hover-color: #1b809e; +$menu-active-color: #176f89; +//Navigation +$nav-bg: transparent; +$nav-color-root: $text-basic; +$nav-color-root-active: $text-basic; +$nav-border-root: $border-basic-color; +$nav-border-root-active: $border-basic-color; +$nav-color-submenu: $text-submenu-active; +$nav-color-submenu-active: $text-submenu-active; +$nav-bg-root: $bg-submenu-active; +$nav-bg-submenu: $bg-submenu-active; +$nav-bg-submenu-active: $bg-submenu-active; +$nav-border-submenu: $border-basic-color; +$nav-submenu-item-border: $border-gray; +$nav-submenu-border-active: $border-basic-color; +//Social Media Share +$social-media-share-bg: transparent; +//Tabs +$tab-heading-bg: $bg-light-gray; +$tab-heading-active-bg: $bg-basic-color; +$tab-heading-color: $text-heading-color; +$tab-heading-active-color: $text-black; +$tab-container-bg: transparent; +$tab-container-border: $border-basic-color; +//Title +$title-bg: transparent; +$title-color: $text-basic; +$title-color-active: $text-basic-active; +//Toggle +$toggle-header-bg: $bg-basic-color; +$toggle-content-bg: $bg-basic-color; +$toggle-show-color: $text-basic-active; +//Search Components +$search-btn-bg: transparent; +$search-btn-active-bg: #e0e0e0; +$search-btn-active-border: #adadad; +//Image component +$image-caption-color: $text-basic; +//Media Link Component +$media-link-bg: transparent; +$media-link-border: $border-basic-color; +$media-link-color: $text-basic; +$media-link-color-active: $text-basic-active; +//Tag Component +$tag-color: $text-basic; +$tag-color-active: $text-basic-active; +$tag-border-active: $border-basic-active; +$tag-link-bg: $bg-blue; +$tag-link-bg-active: $bg-blue-active; +$tag-link-color: $text-white; +//Link Component +$link-bg: transparent; +$link-text-color: $text-basic; +$link-text-color-active: $text-basic-active; +$link-border: $border-basic-color; +//Overlay +$overlay-bg: $bg-light-gray; +//Search Components +$search-title-border: $border-basic-color; +$search-title-color: $text-basic; +$search-item-color: $text-basic; +$search-item-color-active: $text-basic; +$search-item-border: $border-basic-color; +$search-item-border-active: $border-basic-active; +// +//Search Facet Summary +$search-facet-summary-border: transparent; +$search-facet-summary-background: transparent; +$search-facet-summary-item-color: $text-basic; +$search-facet-summary-item-color-horizontal: $text-basic; +$search-facet-summary-item-border: $border-gray; +$search-facet-summary-item-border-horizontal: $border-basic-color; +$search-facet-summary-item-shadow: $border-gray; +$search-facet-summary-clear-border-horizontal: $btn-red; +$search-facet-summary-clear-color: $text-red; +$search-facet-summary-clear-color-horizontal: $text-blue; +// +$search-filter-radius-active: $text-blue; +$search-filter-radius-border: $border-gray; +$search-filter-radius-bg: $border-gray; +// +$search-filter-slider-border-active: $border-basic-color; +$search-filter-slider-bg-active: $bg-blue; +$search-filter-slider-btn-border: $border-gray; +$search-filter-slider-btn-bg: $bg-light-gray; +$search-filter-slider-btn-bg-active: $bg-light-gray-active; +//Serach Pagination +$search-pagination-bg: transparent; +$search-pagination-active-bg: $bg-blue; +$search-pagination-active-color: $text-white; +$search-pagination-hover-color: $text-blue; +$search-pagination-hover-bg: $bg-submenu-active; +$search-pagination-hover-border: $border-basic-active; +//Search selector +$serach-selector-variant-color-active: $text-blue-active; +//Typehead +$tt-color: $text-basic; +$tt-color-active: $text-blue; +$tt-price-color: $text-blue; +$tt-dropdown-bg: $bg-light-gray; +$tt-suggestion-bg-active: $bg-light-gray-active; +$tt-dropdown-border: $border-gray; +//Video +$video-control-bg: $bg-basic-color; +$video-time-color: $text-basic; +$video-time-total-bg: $bg-black; +$video-time-handle-border: $border-gray; +$video-time-handle-bg: $bg-black; +//Form component +$form-bg: transparent; +$form-border: transparent; +$form-color: $text-basic; +//Main +$page-bg: $bg-basic-color; +$page-bg-editor: none; diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/abstracts/vars/_fontSizes.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/abstracts/vars/_fontSizes.scss new file mode 100644 index 000000000000..ca825908578a --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/abstracts/vars/_fontSizes.scss @@ -0,0 +1,16 @@ +$font-small: 11px; +$font-normal: 13px; +$font-medium: 16px; +$font-big: 20px; +$font-extrabig: 24px; +//Navigation +$navigation-font-basic: 18px; +$navigation-font-basic-submenu: 14px; +//Tabs +$tab-font-header: 16px; +//Accordion +$accordion-header: 16px; +//Breadcrumb +$breadcrumb-font: 15px; +// Link List +$link-list-title-font: 15px; diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/abstracts/vars/_margins.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/abstracts/vars/_margins.scss new file mode 100644 index 000000000000..e4e3b68770a1 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/abstracts/vars/_margins.scss @@ -0,0 +1,11 @@ +$default-padding: 5px; +$default-inputs-padding: 12px 10px; + +$extrasmall-margin-bottom: 2px; +$extrasmall-margin: 5px; +$small-margin: 10px; +$middle-margin: 20px; +$large-margin: 30px; +$extralarge-margin: 40px; + +$small-padding: 10px; diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/base/fonts/_fonts.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/base/fonts/_fonts.scss new file mode 100644 index 000000000000..49d54e9f19c9 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/base/fonts/_fonts.scss @@ -0,0 +1 @@ +@import url('https://fonts.googleapis.com/css?family=Open+Sans:300,300i,400,400i,600,600i,700,700i,800,800i&subset=cyrillic,cyrillic-ext,greek,greek-ext,latin-ext,vietnamese'); diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/base/fonts/index.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/base/fonts/index.scss new file mode 100644 index 000000000000..2b1a3796bbc5 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/base/fonts/index.scss @@ -0,0 +1 @@ +@import 'fonts'; diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/base/index.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/base/index.scss new file mode 100644 index 000000000000..72c7ca6829ec --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/base/index.scss @@ -0,0 +1,3 @@ +@import 'fonts'; +@import 'links'; +@import 'reset/ui-datepicker'; diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/base/links/_link-button.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/base/links/_link-button.scss new file mode 100644 index 000000000000..cd6d696c5c09 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/base/links/_link-button.scss @@ -0,0 +1,29 @@ +@import '@sass/abstracts/vars/colors'; +@import '@sass/abstracts/mixins'; + +.button-default, +.default { + @include default-link-button(); + background: #f6f6f6; + color: $text-basic; +} +.button-success, +.success { + @include default-link-button(); + background: $btn-green-light; + color: $text-white; + &:hover { + color: $text-white; + background: $btn-green; + } +} +.button-warning, +.warning { + @include default-link-button(); + background: $btn-red; + color: $text-white; + &:hover { + color: $text-white; + background: $btn-red-active; + } +} diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/base/links/index.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/base/links/index.scss new file mode 100644 index 000000000000..b47c809b056d --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/base/links/index.scss @@ -0,0 +1 @@ +@import 'link-button'; diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/base/reset/_inputs.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/base/reset/_inputs.scss new file mode 100644 index 000000000000..2b76bcb99ef6 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/base/reset/_inputs.scss @@ -0,0 +1,67 @@ +@import '@sass/abstracts/mixins'; +@import '@sass/abstracts/vars'; + +label { + background: transparent; + color: $text-basic; +} +input, +button { + display: inline-block; + box-sizing: border-box; + background: $bg-basic-color !important; + border: 1px solid $border-gray; + border-radius: 0 !important; + color: $text-basic; + font-weight: normal; + padding: 8px 2%; + clear: both; + height: auto; + font-size: $font-normal; +} +input:focus { + border: 1px solid $border-gray; +} +input[type='submit'], +input[type='reset'], +input[type='button'], +button { + border: 1px solid $border-basic-color; + padding: 5px 20px; + &:hover { + background: $bg-light-gray !important; + } +} +input::-moz-focus-inner, +button::-moz-focus-inner { + border: 0; + padding: 0; +} +input[type='text'] { + font-size: 13px; +} +input[type='text'], +input[type='email'], +input[type='password'], +textarea, +select { + box-shadow: inset 0 1px 5px rgba(0, 0, 0, 0.1); + color: $text-basic; + width: 100%; + background: #fff; + border: 1px solid $border-gray; +} + +select { + display: inline-block; + vertical-align: middle; + *vertical-align: auto; + *zoom: 1; + *display: inline; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + outline: none; + cursor: pointer; + padding: 5px; +} diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/base/reset/_links.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/base/reset/_links.scss new file mode 100644 index 000000000000..c83f047bbd14 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/base/reset/_links.scss @@ -0,0 +1,14 @@ +@import '@sass/abstracts/mixins'; +@import '@sass/abstracts/vars'; + +a { + background-color: transparent; + text-decoration: none; + font-size: 1em; + color: $text-basic; + border-bottom: 1px solid $border-basic-color; + &:hover { + color: $text-basic-active; + text-decoration: none; + } +} diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/base/reset/_ui-datepicker.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/base/reset/_ui-datepicker.scss new file mode 100644 index 000000000000..9305794899cf --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/base/reset/_ui-datepicker.scss @@ -0,0 +1,7 @@ +@import '@sass/abstracts/vars'; + +.ui-datepicker-title { + select { + color: $text-basic; + } +} diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/base/richtext/_richtext-files-icons.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/base/richtext/_richtext-files-icons.scss new file mode 100644 index 000000000000..59fe03b50caf --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/base/richtext/_richtext-files-icons.scss @@ -0,0 +1,203 @@ +@import '@fontawesome/scss/mixins'; +@import '@fontawesome/scss/variables'; +@import '@sass/abstracts/mixins'; + +//files icons +.field-filetypeicon > a > span, +.pdf, +.xls, +.xlsx, +.pptx, +.ppt, +.docx, +.doc, +.bmp, +.png, +.jpg, +.jpeg, +.psd, +.gif, +.avi, +.mp4, +.wmv, +.mov, +.mp3, +.wma, +.txt, +.zip { + display: inline-block; + box-sizing: border-box; + font-weight: normal; + width: 15px; + height: 21px; + margin: 0; + padding: 0; + position: relative; + &:before { + @include fa-icon(); + font-size: 16px !important; + position: absolute; + display: block; + top: 50%; + margin: 0; + padding: 0; + transform: translateY(-50%); + } +} +.field-filetypeicon span:before { + content: $fa-var-file-o; +} +.field-filetypeicon { + .pdf:before { + content: $fa-var-file-pdf-o; + } + .xlsx, + .xls { + &:before { + content: $fa-var-file-excel-o; + } + } + .pptx, + .ppt { + &:before { + content: $fa-var-file-powerpoint-o; + } + } + .docx, + .doc { + &:before { + content: $fa-var-file-word-o; + } + } + .bmp, + .png, + .jpg, + .jpeg, + .psd, + .gif, + .tif { + &:before { + content: $fa-var-file-image-o; + } + } + .avi, + .mp4, + .wmv, + .mov, + .mpg, + .mkv, + .vp6, + .vid, + .rv, + .webm, + .swf, + .flv, + .m4v, + .h264, + .mk3d, + .gifv, + .oggv, + .movie, + .divx { + &:before { + content: $fa-var-file-video-o; + } + } + .mp3, + .wma, + .wav, + .fla, + .flac, + .ra, + .rma, + .aif, + .aiff, + .aa, + .aac, + .mid, + .midi, + .aax, + .ac3, + .au, + .ogg, + .avr, + .m4a, + .mp4a, + .amz, + .mka, + .asx, + .pcm, + .m3u, + .xwma { + &:before { + content: $fa-var-file-audio-o; + } + } + .txt:before { + content: $fa-var-file-text-o; + } + .zip, + .zipx, + .rar, + .tar, + .gz, + .dmg, + .iso { + &:before { + content: $fa-var-file-archive-o; + } + } + .css, + .js, + .py, + .git, + .py, + .cpp, + .h, + .ini, + .config { + &:before { + content: $fa-var-file-code-o; + } + } + .exe, + .jar, + .dll, + .bat, + .pl, + .scr, + .msi, + .app, + .deb, + .apk, + .jar, + .vb, + .prg, + .sh { + &:before { + content: $fa-var-cogs; + } + } + .com, + .net, + .org, + .edu, + .gov, + .mil, + .html, + .htm, + .xhtml, + .jhtml, + .php, + .php3, + .php4, + .php5, + .phtmle, + .asp, + .aspx, + .cfm { + &:before { + content: $fa-var-link; + } + } +} diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/base/richtext/_richtext.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/base/richtext/_richtext.scss new file mode 100644 index 000000000000..7d5ec313860d --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/base/richtext/_richtext.scss @@ -0,0 +1,101 @@ +h1, +h2, +h3, +h4, +h5, +h6 { + color: $text-heading-color; +} +h1, +h2 { + margin: $small-margin 0; +} +h3, +h4, +p { + margin: $extrasmall-margin 0; +} +h5, +h6 { + margin: $extrasmall-margin 0 0; +} +strong { + font-weight: 700; +} +ul, +ol { + padding-bottom: $extrasmall-margin; + padding-top: $extrasmall-margin; + margin-left: $small-margin; +} +li { + font-size: $font-normal; + margin-left: 15px; +} +ul { + li { + list-style: disc; + list-style-position: inside; + } +} +ol { + li { + list-style: decimal; + list-style-position: inside; + } +} +a { + color: $rich-content-link-color; + text-decoration: underline; + font-size: 1em; + &:hover { + color: $rich-content-link-color-active; + } +} +table { + height: auto !important; + border: 2px solid $border-gray; + &, + tr, + th, + td { + border: solid 2px $border-gray; + background: $bg-basic-color; + border-collapse: collapse; + vertical-align: middle; + } + tr, + th, + td { + padding: 5px; + } + caption { + background-color: $bg-light-gray; + overflow: hidden; + padding: 10px; + font-size: $font-big; + font-weight: bold; + margin-left: 0; + } + tr { + border: none; + } + th, + td { + border-width: 2px 0 0 2px; + } + th { + background-color: lighten($bg-light-gray, 5%); + font-size: $font-normal; + font-weight: bold; + padding: 7px; + &:first-child { + border-left: 0; + } + } + td { + &:first-child { + border-left: 0; + } + } +} diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/base/richtext/index.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/base/richtext/index.scss new file mode 100644 index 000000000000..1ba07dc731cd --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/base/richtext/index.scss @@ -0,0 +1,2 @@ +@import 'richtext'; +@import 'richtext-files-icons'; diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/base/typehead/_typehead.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/base/typehead/_typehead.scss new file mode 100644 index 000000000000..0365acda25c0 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/base/typehead/_typehead.scss @@ -0,0 +1,95 @@ +@import '@sass/abstracts/vars'; +@import '@sass/abstracts/mixins'; + +.twitter-typeahead { + display: inline-block; + vertical-align: middle; + max-width: 100%; + width: 100%; +} +.tt-menu { + background: $bg-basic-color; + width: 100%; + border: 1px solid $border-gray; +} +.tt-hint { + color: $tt-color; +} +.tt-dropdown-menu { + width: 250px; + margin-top: 5px; + background-color: $tt-dropdown-bg; + border: 1px solid $tt-dropdown-border; + max-height: 300px; + overflow-y: auto; + box-shadow: 0 3px 8px 0 rgba(0, 0, 0, 0.2), 0 0 0 1px rgba(0, 0, 0, 0.08); +} +.tt-suggestion { + padding: 4px 10px; + color: $tt-color; + overflow: hidden; + a { + text-decoration: none; + } + &:last-child { + border: none; + } + .field-image { + width: 100px; + float: left; + margin-right: 10px; + } + .field-make { + font-size: 15px; + float: left; + margin-right: 5px; + } + .field-model { + margin-left: 4px; + font-size: 15px; + font-weight: bold; + width: 100%; + } + .field-priceformatted { + float: right; + font-size: 15px; + color: $tt-price-color; + margin-top: -20px; + } +} +.tt-suggestion.tt-cursor { + color: $tt-color-active; + background-color: $tt-suggestion-bg-active; + cursor: pointer; +} +.loading-in-progress { + .tt-dropdown-menu { + display: block !important; + min-height: 50px; + position: relative; + &:after { + content: ''; + position: absolute; + top: 50%; + left: 50%; + margin-top: -20px; + margin-left: -20px; + height: 40px; + width: 50px; + display: block; + @include loading-gif(); + z-index: 11; + } + &:before { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: $tt-dropdown-bg; + opacity: 0.8; + z-index: 10; + } + } +} diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/base/typehead/index.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/base/typehead/index.scss new file mode 100644 index 000000000000..c0b2466dbfa0 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/base/typehead/index.scss @@ -0,0 +1 @@ +@import 'typehead'; diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/components/_component-column-splitter.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/components/_component-column-splitter.scss new file mode 100644 index 000000000000..0aea965b660a --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/components/_component-column-splitter.scss @@ -0,0 +1,14 @@ +@import '@sass/abstracts/vars'; + +.row.column-splitter { + margin-left: 0; + margin-right: 0; + padding-left: $default-padding / 2; + padding-right: $default-padding / 2; + max-width: none; + + > div { + padding-left: $default-padding; + padding-right: $default-padding; + } +} diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/components/_component-container.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/components/_component-container.scss new file mode 100644 index 000000000000..86bee01402d6 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/components/_component-container.scss @@ -0,0 +1,21 @@ +@import '@sass/abstracts/mixins'; + +.container-wrapper { + width: 100%; +} + +.component { + position: relative; +} + +.container { + padding: 0; + + &.fullwidth-container { + max-width: unset; + } + + .component-content { + @include clearfix(); + } +} diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/components/_component-image.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/components/_component-image.scss new file mode 100644 index 000000000000..92efb3b9ad0a --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/components/_component-image.scss @@ -0,0 +1,18 @@ +@import '@sass/abstracts/vars'; + +.image { + img { + max-width: 100%; + height: auto; + } + a { + display: inline-block; + max-width: 100%; + } + .image-caption { + display: block; + font-style: italic; + font-size: $font-small; + color: $image-caption-color; + } +} diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/components/_component-navigation.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/components/_component-navigation.scss new file mode 100644 index 000000000000..9af44e6e9165 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/components/_component-navigation.scss @@ -0,0 +1,54 @@ +@import '@sass/abstracts/mixins'; +@import '@sass/abstracts/vars'; + +.navigation { + background: $nav-bg; + ul { + padding-left: 0; + } + .level0, + .level1 { + > .navigation-title > a { + font-size: $navigation-font-basic; + border-color: $nav-border-root; + border-style: solid; + border-width: 0 0 2px 0; + padding-right: 10px; + margin-right: 5px; + &:hover { + text-decoration: none; + } + } + } + a { + color: $nav-color-root; + .lt-ie9 &, + .lt-ie9 & span { + color: $nav-color-root; + } + &:focus, + &:hover { + color: $nav-color-root-active; + } + } + li { + &.submenu { + margin-bottom: 0; + } + > .navigation-title { + > a { + padding: 3px 5px 3px 0; + display: block; + color: $nav-color-root; + text-decoration: none; + } + } + > ul a { + font-size: $navigation-font-basic-submenu; + } + } + .submenu > ul { + padding-left: 10px; + } +} +@import '@sass/components/navigation/'; diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/components/_component-promo.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/components/_component-promo.scss new file mode 100644 index 000000000000..77064f569b95 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/components/_component-promo.scss @@ -0,0 +1,42 @@ +@import '@sass/abstracts/vars'; +@import '@sass/abstracts/mixins'; + +.promo { + background: $promo-bg; + padding: 15px; + overflow: hidden; + box-sizing: border-box; + > .component-content { + position: relative; + @include clearfix(); + @include border-basic(all, $promo-border); + + > div { + padding: 5px; + } + } + .zg-height-fix { + .field-promoicon { + margin-top: 100px; + } + } + .field-promoicon { + overflow: hidden; + width: 100%; + img { + width: 100%; + height: auto; + } + } + .field-promolink { + margin-top: 5px; + padding-bottom: 10px; + } + &.image-full-size { + img { + margin-bottom: 10px; + } + } + @import '@sass/variants/promo'; + @import '@sass/base/richtext/richtext'; +} diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/components/_component-richtext-content.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/components/_component-richtext-content.scss new file mode 100644 index 000000000000..6cd5e8c531fc --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/components/_component-richtext-content.scss @@ -0,0 +1,20 @@ +@import '@sass/abstracts/vars'; +@import '@sass/abstracts/mixins'; + +.content, +.rich-text { + border: $rich-content-border; + line-height: 1.5; + font-size: 12px; + overflow: hidden; + * { + max-width: 100%; + } + @import '@sass/base/richtext'; +} +.rich-text { + @import '@sass/variants/rich-text'; +} +.content { + @import '@sass/variants/page-content'; +} diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/components/common/_alignment.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/components/common/_alignment.scss new file mode 100644 index 000000000000..bea37718d81c --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/components/common/_alignment.scss @@ -0,0 +1,26 @@ +.position-left, +.position-left * { + text-align: left !important; +} + +.position-right, +.position-right * { + text-align: right !important; +} + +.position-center, +.position-center * { + text-align: center !important; +} + +.position-left select { + direction: ltr !important; +} + +.position-right select { + direction: rtl !important; +} + +.position-center select { + text-align-last: center !important; +} diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/components/common/_boxed.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/components/common/_boxed.scss new file mode 100644 index 000000000000..2345bcade374 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/components/common/_boxed.scss @@ -0,0 +1,16 @@ +@import '@sass/abstracts/vars'; +@import '@sass/abstracts/mixins'; + +.boxed { + box-sizing: border-box; + margin: 20px 0; + @include respond-to(tablet) { + padding: 10px; + } + @include respond-to(mobile) { + padding: 10px; + } + > div { + @include wrapper(); + } +} diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/components/common/_clearfix.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/components/common/_clearfix.scss new file mode 100644 index 000000000000..0f85b51c7821 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/components/common/_clearfix.scss @@ -0,0 +1,11 @@ +@import '@sass/abstracts/vars'; +@import '@sass/abstracts/mixins'; + +.component-content-clearfix { + .component-content { + @include clearfix; + } +} +.component-clearfix { + @include clearfix; +} diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/components/common/_highlighted.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/components/common/_highlighted.scss new file mode 100644 index 000000000000..d0d37d0bb366 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/components/common/_highlighted.scss @@ -0,0 +1,63 @@ +.highlighted-top { + background: #ffffff; + border-top-width: 3px; + border-top-color: #15909c; + border-style: solid; + padding: 25px; + margin: 0; +} + +.highlighted-top h1, +.highlighted-top h2, +.highlighted-top h3, +.highlighted-top h4 { + margin: 0 0 10px 0; +} + +.highlighted-bottom { + background: #ffffff; + border-bottom-width: 3px; + border-bottom-color: #15909c; + border-style: solid; + padding: 25px; + margin: 0; +} + +.highlighted-bottom h1, +.highlighted-bottom h2, +.highlighted-bottom h3, +.highlighted-bottom h4 { + margin: 0 0 10px 0; +} + +.highlighted-left { + background: #ffffff; + border-left-width: 3px; + border-left-color: #15909c; + border-style: solid; + padding: 25px; + margin: 0; +} + +.highlighted-left h1, +.highlighted-left h2, +.highlighted-left h3, +.highlighted-left h4 { + margin: 0 0 10px 0; +} + +.highlighted-right { + background: #ffffff; + border-right-width: 3px; + border-right-color: #15909c; + border-style: solid; + padding: 25px; + margin: 0; +} + +.highlighted-right h1, +.highlighted-right h2, +.highlighted-right h3, +.highlighted-right h4 { + margin: 0 0 10px 0; +} diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/components/common/_link-button.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/components/common/_link-button.scss new file mode 100644 index 000000000000..ec7cec5df6f9 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/components/common/_link-button.scss @@ -0,0 +1,18 @@ +@import '@sass/abstracts/mixins'; + +.link-button { + @import '@sass/base/links/'; +} +// Promo button +.promo.link-button { + a { + @extend .button-default; + } +} +.link.link-button { + .is-empty-hint, + .field-link span, + a { + @include default-link-button(); + } +} diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/components/common/_promoted-box.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/components/common/_promoted-box.scss new file mode 100644 index 000000000000..7b4ec35ba932 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/components/common/_promoted-box.scss @@ -0,0 +1,3 @@ +.promoted-box { + border: none !important; +} diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/components/common/index.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/components/common/index.scss new file mode 100644 index 000000000000..d3a66a141629 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/components/common/index.scss @@ -0,0 +1,6 @@ +@import 'alignment'; +@import 'boxed'; +@import 'clearfix'; +@import 'highlighted'; +@import 'link-button'; +@import 'promoted-box'; diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/components/container/_bordered.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/components/container/_bordered.scss new file mode 100644 index 000000000000..4510a073599e --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/components/container/_bordered.scss @@ -0,0 +1,24 @@ +@import '@sass/abstracts/vars'; +@import '@sass/abstracts/mixins'; + +.sxa-bordered { + box-sizing: border-box; + > .component-content { + padding: 0; + @include border-basic(); + border-radius: 5px; + } +} + +.column-splitter, +.row-splitter { + box-sizing: border-box; + .sxa-bordered { + padding: $default-padding !important; + > .component, + .scEmptyPlaceholder { + @include border-basic(); + border-radius: 5px; + } + } +} diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/components/container/_title-row-box.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/components/container/_title-row-box.scss new file mode 100644 index 000000000000..76b13e2e0b0f --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/components/container/_title-row-box.scss @@ -0,0 +1,69 @@ +@import '@sass/abstracts/vars'; +@import '@sass/abstracts/mixins'; + +.title-row-box { + @include border-basic(); + box-sizing: border-box; + width: 100%; + height: 87px; + margin: 0; + padding: 0; + background: $container-title-row-bg; + > .component-content { + @include wrapper(); + .title { + display: inline-block; + vertical-align: middle; + box-sizing: border-box; + margin: 23px 0 0 0; + padding: 0; + h1 { + @include font-size(2.2); + border: none; + } + @include respond-to(all-mobile) { + margin: 23px 0 0 20px; + } + } + .rich-text { + margin: 0; + h1, + h2, + h3, + h4 { + margin: 0; + padding: 0; + } + } + .breadcrumb { + display: inline-block; + vertical-align: middle; + box-sizing: border-box; + @include font-size(1.4); + margin: 23px 0 0 0; + padding: 0; + float: right; + clear: both; + @include respond-to(tablet) { + margin: 23px 20px 0 0; + } + @include respond-to(mobile) { + display: none; + } + } + } + @include respond-to(all-mobile) { + .alpha { + vertical-align: middle; + box-sizing: border-box; + width: auto !important; //overrides grid + float: left; + } + .omega { + vertical-align: middle; + box-sizing: border-box; + width: auto !important; //overrides grid + float: right; + } + } +} diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/components/container/index.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/components/container/index.scss new file mode 100644 index 000000000000..8b09323a3e32 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/components/container/index.scss @@ -0,0 +1 @@ +@import 'bordered'; diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/components/image-alignment/_image-left.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/components/image-alignment/_image-left.scss new file mode 100644 index 000000000000..5be626a2a589 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/components/image-alignment/_image-left.scss @@ -0,0 +1,3 @@ +.image-left .component-content > div > img { + float: left; +} diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/components/image-alignment/_image-right.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/components/image-alignment/_image-right.scss new file mode 100644 index 000000000000..07fd291c664b --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/components/image-alignment/_image-right.scss @@ -0,0 +1,3 @@ +.image-right .component-content > div > img { + float: right; +} diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/components/image/_image-banner.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/components/image/_image-banner.scss new file mode 100644 index 000000000000..fb8b877ea90e --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/components/image/_image-banner.scss @@ -0,0 +1,15 @@ +.hero-banner { + .component-content { + background-position: center; + background-repeat: no-repeat; + background-size: cover; + height: 800px; + + @include respond-to(mobile-large) { + height: 300px; + } + } + .sc-image-wrapper { + opacity: 0; + } +} diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/components/image/_image-default-size.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/components/image/_image-default-size.scss new file mode 100644 index 000000000000..7e1771ff5c1d --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/components/image/_image-default-size.scss @@ -0,0 +1,6 @@ +.image-default-size { + img { + max-width: none; + width: auto !important; + } +} diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/components/image/index.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/components/image/index.scss new file mode 100644 index 000000000000..5a79b6155e8b --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/components/image/index.scss @@ -0,0 +1,2 @@ +@import 'image-default-size'; +@import 'image-banner'; diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/components/index.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/components/index.scss new file mode 100644 index 000000000000..d93583e8a490 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/components/index.scss @@ -0,0 +1,17 @@ +@import 'component-column-splitter'; +@import 'component-container'; +@import 'component-image'; +@import 'component-navigation'; +@import 'component-promo'; +@import '_component-richtext-content'; + +@import 'common'; +@import 'container'; +@import 'layout'; +@import 'spacing'; +@import 'promo'; +@import 'spacing'; +@import 'title'; +@import 'image'; +@import 'link-list'; +@import 'rich-text'; diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/components/layout/_acaindent.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/components/layout/_acaindent.scss new file mode 100644 index 000000000000..b435f32d93d8 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/components/layout/_acaindent.scss @@ -0,0 +1,5 @@ +@import '@sass/abstracts/vars'; + +.alan-indent { + margin: 0 $extralarge-margin; +} diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/components/layout/_background.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/components/layout/_background.scss new file mode 100644 index 000000000000..b11ee2982906 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/components/layout/_background.scss @@ -0,0 +1,27 @@ +@import '@sass/abstracts/vars'; +@import '@sass/abstracts/mixins'; + +%cover-bg { + background-repeat: no-repeat; + background-size: cover; +} +.cover-background { + > .component-content { + @extend %cover-bg; + } +} +.fix-background { + > .component-content { + @extend %cover-bg; + @include fixed-bg(left top); + } +} +.parallax-background { + > .component-content { + @extend %cover-bg; + @include fixed-bg(50% 0); + @include respond-to(mobile-large) { + background-attachment: scroll; + } + } +} diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/components/layout/index.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/components/layout/index.scss new file mode 100644 index 000000000000..38924a747719 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/components/layout/index.scss @@ -0,0 +1 @@ +@import 'background'; diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/components/link-list/_component-link-list.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/components/link-list/_component-link-list.scss new file mode 100644 index 000000000000..9441f03c26c7 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/components/link-list/_component-link-list.scss @@ -0,0 +1,50 @@ +@import '@sass/abstracts/mixins'; +@import '@sass/abstracts/vars'; +@import '@fontawesome/scss/mixins'; +@import '@fontawesome/scss/variables'; + +.link-list { + background: $link-list-bg; + h1, + h2, + h3, + h4, + h5, + h6 { + @include border-basic(bottom, $link-list-header-border); + } + > .component-content { + ul { + background: $link-list-items-bg; + } + li { + background: $link-list-item-bg; + display: block; + font-size: $font-normal; + a { + display: inline; + color: $link-list-item-color; + position: relative; + padding-left: 10px; + font-size: 1em; + text-decoration: none; + &:before { + @include fa-icon(); + content: $fa-var-chevron-right; + position: absolute; + left: 0; + top: 50%; + transform: translateY(-50%); + font-size: 10px; + } + &:hover { + color: $link-list-item-color-active; + text-decoration: none; + border-bottom: 1px solid $link-list-item-border-active; + } + } + } + } + @import '../../base/links'; + @import '../../variants/link-list'; +} diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/components/link-list/_list-vertical.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/components/link-list/_list-vertical.scss new file mode 100644 index 000000000000..50f71e6ff90a --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/components/link-list/_list-vertical.scss @@ -0,0 +1,19 @@ +@import '@sass/abstracts/vars'; +@import '@sass/abstracts/mixins'; + +.link-list.list-vertical { + h3 { + background: $bg-basic-color; + width: 100%; + display: inline-block; + padding: 3px 5px; + @include border-basic(); + } + a { + border: none; + } + li { + display: block; + margin-left: $middle-margin; + } +} diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/components/link-list/index.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/components/link-list/index.scss new file mode 100644 index 000000000000..cf000d7cda70 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/components/link-list/index.scss @@ -0,0 +1,2 @@ +@import 'component-link-list'; +@import 'list-vertical'; diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/components/navigation/_navigation-fat.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/components/navigation/_navigation-fat.scss new file mode 100644 index 000000000000..06453f04f38e --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/components/navigation/_navigation-fat.scss @@ -0,0 +1,57 @@ +@import '@sass/abstracts/vars'; +@import '@sass/abstracts/mixins'; +@import '@fontawesome/scss/mixins'; +@import '@fontawesome/scss/variables'; + +.navigation.navigation-fat { + background: $bg-basic-color; + @include border-basic(); + + a { + text-decoration: none; + } + + nav > ul { + list-style: none; + padding: 10px 0; + overflow: hidden; + } + + .rel-level1 { + margin: $small-margin; + padding: 0; + display: inline-block; + vertical-align: top; + + &.submenu ul a { + position: relative; + &:before { + @include fa-icon(); + transform: translateY(-50%); + position: absolute; + content: $fa-var-chevron-right; + top: 50%; + left: -9px; + font-size: 10px; + } + } + > .navigation-title > a { + @include border-basic(top, $border-basic-color, 2px); + border-bottom: 0; + background: $nav-bg-root; + padding: 5px 10px; + display: block; + width: auto; + &:hover { + border-color: $menu-hover-color; + } + } + > ul { + padding: 0; + } + } + .rel-level2 { + padding-left: $small-margin; + display: block; + } +} diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/components/navigation/_navigation-main-horizontal-vertical.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/components/navigation/_navigation-main-horizontal-vertical.scss new file mode 100644 index 000000000000..973ea5007f3f --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/components/navigation/_navigation-main-horizontal-vertical.scss @@ -0,0 +1,158 @@ +@import '@sass/abstracts/vars'; +@import '@sass/abstracts/mixins'; +@import '@fontawesome/scss/mixins'; +@import '@fontawesome/scss/variables'; + +$borderSize: 2px; + +//Drop Down Navigation Common Part +.navigation.navigation-main { + .component-content > nav { + position: relative; + ul { + @include clearfix(); + } + } + .rel-level1 { + float: left; + &.active { + > .navigation-title > a { + border-color: $nav-border-root; + } + } + > .navigation-title > a { + border-width: 2px 0 0 0; + border-color: transparent; + &:hover { + border-color: $nav-border-root; + } + } + &.submenu { + > .navigation-title a { + position: relative; + padding-right: 15px; + &:after { + @include fa-icon(); + font-size: 10px; + content: $fa-var-chevron-down; + position: absolute; + transform: translateY(-50%); + top: 50%; + right: 0; + } + } + } + //submenu + > ul { + overflow: hidden; + display: none; + z-index: 10; + position: absolute; + top: 100%; + background: $nav-bg-submenu; + margin-left: 0; + margin-top: -$borderSize; + border-top: $borderSize solid $nav-border-submenu; + border-left: 1px solid $nav-submenu-item-border; + border-bottom: 1px solid $nav-submenu-item-border; + } + &:active, + &:hover { + > a { + & + ul { + display: block !important; + } + } + > ul { + display: block !important; + min-width: 60px; + } + } + &.active { + > .navigation-title > a:link, + > .navigation-title > a:visited { + color: $text-basic-active; + } + > span { + display: block; + padding: 10px 20px; + color: $text-white; + } + } + &.submenu.active { + border-color: $menu-active-color; + } + } + .rel-level2 { + &:hover, + &:focus { + transition: background 0.2s ease-in; + background: $nav-bg-submenu-active; + } + div > a { + display: block; + font-weight: normal; + font-size: $navigation-font-basic-submenu; + text-align: center; + &:hover, + &:focus { + color: $nav-color-submenu; + } + } + &.submenu { + box-sizing: border-box; + &.navigation-image { + text-align: center; + > a, + .field-navigationtext { + text-align: left; + } + } + } + } + // Additional styles fot drop down horizontal navigation + &.navigation-main-horizontal { + .submenu > ul { + padding-left: 0; + } + .rel-level1 { + &:active, + &:hover { + > a { + & + ul { + display: inline-flex !important; + flex-wrap: wrap; + } + } + > ul { + display: inline-flex !important; + flex-wrap: wrap; + } + } + > ul { + width: 100%; + clear: both; + left: 0; + } + } + .rel-level2 { + float: left; + padding: 10px; + @include border-basic(right, $nav-submenu-item-border, 1px); + flex-grow: 1; + flex-basis: 23%; + } + } + // Additional styles fot drop down vertical navigation + &.navigation-main-vertical { + .rel-level1 > ul { + padding-left: 0; + width: auto; + left: auto; + @include border-basic(right, $nav-submenu-item-border, 1px); + > li { + padding: 5px 10px; + } + } + } +} diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/components/navigation/_navigation-mobile.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/components/navigation/_navigation-mobile.scss new file mode 100644 index 000000000000..33b534c557c5 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/components/navigation/_navigation-mobile.scss @@ -0,0 +1,89 @@ +@import '@sass/abstracts/vars'; +@import '@sass/abstracts/mixins'; +@import '@fontawesome/scss/mixins'; +@import '@fontawesome/scss/variables'; + +.navigation.navigation-mobile { + nav > ul { + border: none; + @include border-basic(); + } + .rel-level1 { + cursor: pointer; + margin: 0; + background: $bg-basic-color; + > ul { + li { + position: relative; + a:before { + @include fa-icon(); + content: $fa-var-chevron-right; + display: block; + position: absolute; + left: 0; + font-size: 10px; + top: 20px; + transform: translateY(-50%); + } + } + padding-left: 25px; + display: none; + background: $nav-bg-submenu; + } + &.submenu { + > ul { + display: none; + } + &:focus { + > .navigation-title:before { + transform: rotate(180deg); + transition: 0.6s; + transform-style: preserve-3d; + } + > ul { + display: block; + } + } + > .navigation-title { + position: relative; + &:before { + @include fa-icon(); + content: $fa-var-chevron-down; + display: block; + position: absolute; + right: 10px; + font-size: 10px; + top: 20px; + transform: translateY(-50%); + transform: rotate(0); + transition: 0.6s; + transform-style: preserve-3d; + } + } + } + > .navigation-title { + border-bottom: 1px solid $border-basic-color; + margin: 0; + &:last-child { + border-bottom: none; + } + } + .navigation-title { + padding: 10px; + > a { + text-decoration: none; + color: $text-basic; + display: inline; + } + } + } + .rel-level2 { + display: list-item; + .navigation-title > a { + font-weight: 500; + } + ul { + display: none; + } + } +} diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/components/navigation/_navigation-sidebar.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/components/navigation/_navigation-sidebar.scss new file mode 100644 index 000000000000..0371680494f5 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/components/navigation/_navigation-sidebar.scss @@ -0,0 +1,29 @@ +@import '@sass/abstracts/vars'; +@import '@sass/abstracts/mixins'; + +.navigation.navigation-sidebar { + a { + text-decoration: none; + } + nav > ul { + .rel-level1 { + > .navigation-title { + margin-bottom: 0; + > a { + padding: 5px 10px; + display: block; + position: relative; + z-index: 1; + margin-right: 0; + } + } + > ul { + margin-left: $small-margin; + margin-top: $extrasmall-margin; + } + } + .rel-level2 { + display: block; + } + } +} diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/components/navigation/_sitemap-navigation.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/components/navigation/_sitemap-navigation.scss new file mode 100644 index 000000000000..95725b916497 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/components/navigation/_sitemap-navigation.scss @@ -0,0 +1,20 @@ +@import '@sass/abstracts/vars'; +@import '@sass/abstracts/mixins'; +@import '@fontawesome/scss/mixins'; +@import '@fontawesome/scss/variables'; + +.navigation.sitemap-navigation { + .level2 a { + position: relative; + padding-left: 10px; + &:before { + @include font-size(1.4); + @include fa-icon(); + transform: translateY(-50%) scale(1, -1); + position: absolute; + content: $fa-var-share; + top: 50%; + left: -8px; + } + } +} diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/components/navigation/index.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/components/navigation/index.scss new file mode 100644 index 000000000000..6850050e43c8 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/components/navigation/index.scss @@ -0,0 +1,5 @@ +@import 'navigation-main-horizontal-vertical'; +@import 'navigation-mobile'; +@import 'navigation-sidebar'; +@import 'sitemap-navigation'; +@import 'navigation-fat'; diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/components/promo/_absolute-bottom-link.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/components/promo/_absolute-bottom-link.scss new file mode 100644 index 000000000000..f6589e1802bb --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/components/promo/_absolute-bottom-link.scss @@ -0,0 +1,8 @@ +.promo.absolute-bottom-link { + position: relative; + .field-promolink { + position: absolute; + bottom: 10px; + right: 10px; + } +} diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/components/promo/_promo-hero.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/components/promo/_promo-hero.scss new file mode 100644 index 000000000000..9845d6470142 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/components/promo/_promo-hero.scss @@ -0,0 +1,42 @@ +@import '@sass/abstracts/vars'; +@import '@sass/abstracts/mixins'; + +.promo.promo-hero { + position: relative; + text-align: center; + &.promo-hero-half { + float: left; + width: 50%; + @include respond-to(all-mobile) { + float: none; + width: 100%; + } + } + .field-promotext { + display: inline-block; + transform: translateY(-50%); + position: absolute; + top: 50%; + left: 0; + right: 0; + padding: 20px; + color: $promo-hero-text-color; + background: $promo-bg-hero; + > a { + color: inherit; + text-decoration: none; + } + @include respond-to(all-mobile) { + width: 100%; + margin: 0; + h1 { + @include font-size(2.4); + } + h2, + h3, + h4 { + @include font-size(2); + } + } + } +} diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/components/promo/_promo-shadow.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/components/promo/_promo-shadow.scss new file mode 100644 index 000000000000..be34b9fc1a5b --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/components/promo/_promo-shadow.scss @@ -0,0 +1,43 @@ +@import '@sass/abstracts/vars'; +@import '@sass/abstracts/mixins'; + +.promo-shadow { + max-width: 960px; + padding: 0; + border-top-width: 3px; + border-top-color: $promo-shadow-border; + border-style: solid; + overflow: visible; + position: relative; + &.promo { + float: left; + } + > .component-content { + @include respond-to(all-mobile) { + margin: 0 10px 30px 10px; + } + padding: 15px; + margin: 0 0 30px 0; + &:before, + &:after { + opacity: 0.7; + box-shadow: 0 17px 10px rgba(0, 0, 0, 0.7); + position: absolute; + z-index: -1; + height: 20%; + max-height: 100px; + max-width: 460px; + width: 47%; + content: ''; + bottom: 10px; + } + &:before { + left: 2%; + transform: rotate(-3deg); + } + &:after { + right: 2%; + transform: rotate(3deg); + } + } +} diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/components/promo/index.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/components/promo/index.scss new file mode 100644 index 000000000000..1c71782f6097 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/components/promo/index.scss @@ -0,0 +1,3 @@ +@import 'absolute-bottom-link'; +@import 'promo-hero'; +@import 'promo-shadow'; diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/components/rich-text/_rich-text-lists.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/components/rich-text/_rich-text-lists.scss new file mode 100644 index 000000000000..9d7b3bc88ba1 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/components/rich-text/_rich-text-lists.scss @@ -0,0 +1,63 @@ +@import '@sass/abstracts/vars'; +@import '@sass/abstracts/mixins'; + +.rich-text-lists, +.field-featurelist { + ul { + box-sizing: border-box; + margin: 0; + padding: 0; + li { + @include font-size(1.3); + position: relative; + line-height: 21px; + list-style: none; + margin: 0; + padding: 5px 0 5px 25px; + &:before { + display: inline-block; + vertical-align: middle; + @include font-size(1.2); + font-family: 'FontAwesome', sans-serif; + position: absolute; + top: 3px; + left: 0; + color: $text-basic; + text-align: center; + content: '\f00c'; + width: 20px; + margin: 0 5px 0 0; + padding: 0; + } + } + } +} +.field-featurelist { + ul { + li { + &:before { + display: inline-block; + vertical-align: middle; + @include font-size(1.2); + font-family: 'FontAwesome', sans-serif; + position: absolute; + top: 3px; + left: 0; + color: $text-basic; + text-align: center; + content: '\f046'; + width: 20px; + margin: 0 5px 0 0; + padding: 0; + } + } + } +} +.media-link { + &.file-type-icon-media-link { + float: left; + .field-filetypeicon { + text-align: center; + } + } +} diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/components/rich-text/index.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/components/rich-text/index.scss new file mode 100644 index 000000000000..6e89c6a0e185 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/components/rich-text/index.scss @@ -0,0 +1 @@ +@import 'rich-text-lists'; diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/components/spacing/_background-colors.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/components/spacing/_background-colors.scss new file mode 100644 index 000000000000..30afeef82ee4 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/components/spacing/_background-colors.scss @@ -0,0 +1,14 @@ +@import '@sass/abstracts/vars'; + +.container-gray-background { + background: $bg-light-gray; +} +.container-clean-background { + background: $bg-basic-color; +} +.container-dark-background { + background: $bg-black-active; +} +.container-color-background { + background: $bg-blue; +} diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/components/spacing/_indent.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/components/spacing/_indent.scss new file mode 100644 index 000000000000..9d2f599e66b0 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/components/spacing/_indent.scss @@ -0,0 +1,10 @@ +@import '@sass/abstracts/vars'; +.indent { + margin: 0 $extralarge-margin; +} +.indent-top { + margin-top: $middle-margin; +} +.indent-bottom { + margin-bottom: $middle-margin; +} diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/components/spacing/index.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/components/spacing/index.scss new file mode 100644 index 000000000000..6d31d378d6dd --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/components/spacing/index.scss @@ -0,0 +1,2 @@ +@import 'background-colors'; +@import 'indent'; diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/components/title/_component-title.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/components/title/_component-title.scss new file mode 100644 index 000000000000..4fe0e836f3d6 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/components/title/_component-title.scss @@ -0,0 +1,25 @@ +@import '@sass/abstracts/vars'; +@import '@sass/abstracts/mixins'; + +.title { + background: $title-bg; + h1, + .field-title { + > a, + > span { + @include border-basic(bottom, $border-basic-color); + font-size: $font-extrabig; + margin-bottom: $small-margin; + color: $title-color; + line-height: normal; + padding-bottom: 10px; + display: block; + text-decoration: none; + cursor: pointer; + &:hover { + color: $title-color-active; + } + } + } + @import '@sass/variants/title'; +} diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/components/title/index.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/components/title/index.scss new file mode 100644 index 000000000000..f91198792de8 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/components/title/index.scss @@ -0,0 +1 @@ +@import 'component-title'; diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/main.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/main.scss new file mode 100644 index 000000000000..87f251596147 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/main.scss @@ -0,0 +1,4 @@ +@import 'base'; +@import 'components'; +@import 'variants'; +@import 'app'; diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/variants/index.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/variants/index.scss new file mode 100644 index 000000000000..5064fee7c256 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/assets/sass/variants/index.scss @@ -0,0 +1,6 @@ +@import 'link-list'; +@import 'navigation'; +@import 'page-content'; +@import 'promo'; +@import 'rich-text'; +@import 'title'; diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/variants/link-list/index.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/variants/link-list/index.scss new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/variants/navigation/index.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/variants/navigation/index.scss new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/variants/page-content/index.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/variants/page-content/index.scss new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/variants/promo/index.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/variants/promo/index.scss new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/variants/rich-text/index.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/variants/rich-text/index.scss new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/examples/cms-sitecore-xmcloud/src/assets/sass/variants/title/index.scss b/examples/cms-sitecore-xmcloud/src/assets/sass/variants/title/index.scss new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/examples/cms-sitecore-xmcloud/src/components/ColumnSplitter.tsx b/examples/cms-sitecore-xmcloud/src/components/ColumnSplitter.tsx new file mode 100644 index 000000000000..995778c92f1d --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/components/ColumnSplitter.tsx @@ -0,0 +1,65 @@ +import React from 'react' +import { + ComponentParams, + ComponentRendering, + Placeholder, +} from '@sitecore-jss/sitecore-jss-nextjs' + +interface ComponentProps { + rendering: ComponentRendering & { params: ComponentParams } + params: ComponentParams +} + +export const Default = (props: ComponentProps): JSX.Element => { + const styles = `${props.params.GridParameters ?? ''} ${ + props.params.Styles ?? '' + }`.trimEnd() + const columnWidths = [ + props.params.ColumnWidth1, + props.params.ColumnWidth2, + props.params.ColumnWidth3, + props.params.ColumnWidth4, + props.params.ColumnWidth5, + props.params.ColumnWidth6, + props.params.ColumnWidth7, + props.params.ColumnWidth8, + ] + const columnStyles = [ + props.params.Styles1, + props.params.Styles2, + props.params.Styles3, + props.params.Styles4, + props.params.Styles5, + props.params.Styles6, + props.params.Styles7, + props.params.Styles8, + ] + const enabledPlaceholders = props.params.EnabledPlaceholders.split(',') + const id = props.params.RenderingIdentifier + + return ( +
+ {enabledPlaceholders.map((ph, index) => { + const phKey = `column-${ph}-{*}` + const phStyles = `${columnWidths[+ph - 1]} ${ + columnStyles[+ph - 1] ?? '' + }`.trimEnd() + + return ( +
+
+ +
+
+ ) + })} +
+ ) +} diff --git a/examples/cms-sitecore-xmcloud/src/components/Container.tsx b/examples/cms-sitecore-xmcloud/src/components/Container.tsx new file mode 100644 index 000000000000..b880da06a9d0 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/components/Container.tsx @@ -0,0 +1,67 @@ +import React from 'react' +import { + ComponentParams, + ComponentRendering, + Placeholder, + useSitecoreContext, +} from '@sitecore-jss/sitecore-jss-nextjs' + +const BACKGROUND_REG_EXP = new RegExp( + /[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}/gi +) + +interface ComponentProps { + rendering: ComponentRendering & { params: ComponentParams } + params: ComponentParams +} + +const DefaultContainer = (props: ComponentProps): JSX.Element => { + const { sitecoreContext } = useSitecoreContext() + const containerStyles = + props.params && props.params.Styles ? props.params.Styles : '' + const styles = `${props.params.GridParameters} ${containerStyles}`.trimEnd() + const phKey = `container-${props.params.DynamicPlaceholderId}` + const id = props.params.RenderingIdentifier + let backgroundImage = props.params.BackgroundImage as string + let backgroundStyle: { [key: string]: string } = {} + + if (backgroundImage) { + const prefix = `${ + sitecoreContext.pageState !== 'normal' ? '/sitecore/shell' : '' + }/-/media/` + backgroundImage = `${backgroundImage + ?.match(BACKGROUND_REG_EXP) + ?.pop() + ?.replace(/-/gi, '')}` + backgroundStyle = { + backgroundImage: `url('${prefix}${backgroundImage}')`, + } + } + + return ( +
+
+
+ +
+
+
+ ) +} + +export const Default = (props: ComponentProps): JSX.Element => { + const splitStyles = props.params?.Styles?.split(' ') + + if (splitStyles && splitStyles.includes('container')) { + return ( +
+ +
+ ) + } + + return +} diff --git a/examples/cms-sitecore-xmcloud/src/components/ContentBlock.tsx b/examples/cms-sitecore-xmcloud/src/components/ContentBlock.tsx new file mode 100644 index 000000000000..33540cb07ea8 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/components/ContentBlock.tsx @@ -0,0 +1,29 @@ +import { + Text, + RichText, + Field, + withDatasourceCheck, +} from '@sitecore-jss/sitecore-jss-nextjs' +import { ComponentProps } from 'lib/component-props' + +type ContentBlockProps = ComponentProps & { + fields: { + heading: Field + content: Field + } +} + +/** + * A simple Content Block component, with a heading and rich text block. + * This is the most basic building block of a content site, and the most basic + * JSS component that's useful. + */ +const ContentBlock = ({ fields }: ContentBlockProps): JSX.Element => ( +
+ + + +
+) + +export default withDatasourceCheck()(ContentBlock) diff --git a/examples/cms-sitecore-xmcloud/src/components/FEaaSWrapper.tsx b/examples/cms-sitecore-xmcloud/src/components/FEaaSWrapper.tsx new file mode 100644 index 000000000000..00bc5756f1e6 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/components/FEaaSWrapper.tsx @@ -0,0 +1,18 @@ +import { + FEaaSComponent, + FEaaSComponentProps, +} from '@sitecore-jss/sitecore-jss-nextjs' +import React from 'react' + +export const Default = (props: FEaaSComponentProps): JSX.Element => { + const styles = `component feaas ${props.params?.styles}`.trimEnd() + const id = props.params?.RenderingIdentifier + + return ( +
+
+ +
+
+ ) +} diff --git a/examples/cms-sitecore-xmcloud/src/components/Image.tsx b/examples/cms-sitecore-xmcloud/src/components/Image.tsx new file mode 100644 index 000000000000..07032ebd373a --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/components/Image.tsx @@ -0,0 +1,97 @@ +import React from 'react' +import { + Image as JssImage, + Link as JssLink, + ImageField, + Field, + LinkField, + Text, + useSitecoreContext, +} from '@sitecore-jss/sitecore-jss-nextjs' + +interface Fields { + Image: ImageField + ImageCaption: Field + TargetUrl: LinkField +} + +type ImageProps = { + params: { [key: string]: string } + fields: Fields +} + +const ImageDefault = (props: ImageProps): JSX.Element => ( +
+
+ Image +
+
+) + +export const Banner = (props: ImageProps): JSX.Element => { + const { sitecoreContext } = useSitecoreContext() + const backgroundStyle = { + backgroundImage: `url('${props?.fields?.Image?.value?.src}')`, + } + const modifyImageProps = { + ...props.fields.Image, + editable: props?.fields?.Image?.editable + ?.replace(`width="${props?.fields?.Image?.value?.width}"`, 'width="100%"') + .replace( + `height="${props?.fields?.Image?.value?.height}"`, + 'height="100%"' + ), + } + const id = props.params.RenderingIdentifier + + return ( +
+
+ {sitecoreContext.pageEditing ? ( + + ) : ( + '' + )} +
+
+ ) +} + +export const Default = (props: ImageProps): JSX.Element => { + const { sitecoreContext } = useSitecoreContext() + + if (props.fields) { + const Image = () => + const id = props.params.RenderingIdentifier + + return ( +
+
+ {sitecoreContext.pageState === 'edit' ? ( + + ) : ( + + + + )} + +
+
+ ) + } + + return +} diff --git a/examples/cms-sitecore-xmcloud/src/components/LinkList.tsx b/examples/cms-sitecore-xmcloud/src/components/LinkList.tsx new file mode 100644 index 000000000000..7133ade7d00c --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/components/LinkList.tsx @@ -0,0 +1,92 @@ +import React from 'react' +import { + Link as JssLink, + Text, + LinkField, + TextField, +} from '@sitecore-jss/sitecore-jss-nextjs' + +type ResultsFieldLink = { + field: { + link: LinkField + } +} + +interface Fields { + data: { + datasource: { + children: { + results: ResultsFieldLink[] + } + field: { + title: TextField + } + } + } +} + +type LinkListProps = { + params: { [key: string]: string } + fields: Fields +} + +type LinkListItemProps = { + key: string + index: number + total: number + field: LinkField +} + +const LinkListItem = (props: LinkListItemProps) => { + let className = `item${props.index}` + className += (props.index + 1) % 2 === 0 ? ' even' : ' odd' + if (props.index === 0) { + className += ' first' + } + if (props.index + 1 === props.total) { + className += ' last' + } + return ( +
  • +
    + +
    +
  • + ) +} + +export const Default = (props: LinkListProps): JSX.Element => { + const datasource = props.fields?.data?.datasource + const styles = `component link-list ${props.params.styles}`.trimEnd() + const id = props.params.RenderingIdentifier + + if (datasource) { + const list = datasource.children.results + .filter((element: ResultsFieldLink) => element?.field?.link) + .map((element: ResultsFieldLink, key: number) => ( + + )) + + return ( +
    +
    + +
      {list}
    +
    +
    + ) + } + + return ( +
    +
    +

    Link List

    +
    +
    + ) +} diff --git a/examples/cms-sitecore-xmcloud/src/components/Navigation.tsx b/examples/cms-sitecore-xmcloud/src/components/Navigation.tsx new file mode 100644 index 000000000000..5733d28d8316 --- /dev/null +++ b/examples/cms-sitecore-xmcloud/src/components/Navigation.tsx @@ -0,0 +1,170 @@ +import React, { useState } from 'react' +import { + Link, + LinkField, + Text, + TextField, + useSitecoreContext, +} from '@sitecore-jss/sitecore-jss-nextjs' + +interface Fields { + Id: string + DisplayName: string + Title: TextField + NavigationTitle: TextField + Href: string + Querystring: string + Children: Array + Styles: string[] +} + +type NavigationProps = { + params?: { [key: string]: string } + fields: Fields + handleClick: (event?: React.MouseEvent) => void + relativeLevel: number +} + +const getNavigationText = function ( + props: NavigationProps +): JSX.Element | string { + let text + + if (props.fields.NavigationTitle) { + text = + } else if (props.fields.Title) { + text = + } else { + text = props.fields.DisplayName + } + + return text +} + +const getLinkTitle = (props: NavigationProps): string | undefined => { + let title + if (props.fields.NavigationTitle?.value) { + title = props.fields.NavigationTitle.value.toString() + } else if (props.fields.Title?.value) { + title = props.fields.Title.value.toString() + } else { + title = props.fields.DisplayName + } + + return title +} + +const getLinkField = (props: NavigationProps): LinkField => ({ + value: { + href: props.fields.Href, + title: getLinkTitle(props), + querystring: props.fields.Querystring, + }, +}) + +const NavigationList = (props: NavigationProps) => { + const { sitecoreContext } = useSitecoreContext() + + let children: JSX.Element[] = [] + if (props.fields.Children && props.fields.Children.length) { + children = props.fields.Children.map((element: Fields, index: number) => ( + + )) + } + + return ( +
  • +
    + + {getNavigationText(props)} + +
    + {children.length > 0 ?
      {children}
    : null} +
  • + ) +} + +export const Default = (props: NavigationProps): JSX.Element => { + const [isOpenMenu, openMenu] = useState(false) + const { sitecoreContext } = useSitecoreContext() + const styles = + props.params != null + ? `${props.params.GridParameters ?? ''} ${ + props.params.Styles ?? '' + }`.trimEnd() + : '' + const id = props.params != null ? props.params.RenderingIdentifier : null + + if (!Object.values(props.fields).length) { + return ( +
    +
    [Navigation]
    +
    + ) + } + + const handleToggleMenu = ( + event?: React.MouseEvent, + flag?: boolean + ): void => { + if (event && sitecoreContext?.pageEditing) { + event.preventDefault() + } + + if (flag !== undefined) { + return openMenu(flag) + } + + openMenu(!isOpenMenu) + } + + const list = Object.values(props.fields) + .filter((element) => element) + .map((element: Fields, key: number) => ( + ) => + handleToggleMenu(event, false) + } + relativeLevel={1} + /> + )) + + return ( +
    +