Feat/BOOK-240 hero video
Approved-by: Chuma Mcphoy (We Ahead) Approved-by: Christel Westerberg
This commit is contained in:
@@ -53,6 +53,7 @@
|
||||
"./utils/chunk": "./utils/chunk.ts",
|
||||
"./utils/dateFormatting": "./utils/dateFormatting.ts",
|
||||
"./utils/debounce": "./utils/debounce.ts",
|
||||
"./utils/focalPoint": "./utils/focalPoint.ts",
|
||||
"./utils/imageVault": "./utils/imageVault.ts",
|
||||
"./utils/isDefined": "./utils/isDefined.ts",
|
||||
"./utils/isEdge": "./utils/isEdge.ts",
|
||||
|
||||
11
packages/common/utils/focalPoint.ts
Normal file
11
packages/common/utils/focalPoint.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { z } from "zod"
|
||||
|
||||
export const focalPointSchema = z
|
||||
.object({
|
||||
x: z.number().nullish(),
|
||||
y: z.number().nullish(),
|
||||
})
|
||||
.transform(({ x, y }) => ({
|
||||
x: x ?? 50,
|
||||
y: y ?? 50,
|
||||
}))
|
||||
@@ -1,9 +1,6 @@
|
||||
import { z } from "zod"
|
||||
|
||||
const focalPointSchema = z.object({
|
||||
x: z.number(),
|
||||
y: z.number(),
|
||||
})
|
||||
import { focalPointSchema } from "./focalPoint"
|
||||
|
||||
const deprecatedMetaDataSchema = z.object({
|
||||
DefinitionType: z.number().nullish(),
|
||||
|
||||
@@ -18,8 +18,13 @@
|
||||
|
||||
@media (hover: hover) {
|
||||
&:hover .iconWrapper {
|
||||
background-color: var(--Component-Button-Inverted-Fill-Hover);
|
||||
color: var(--Component-Button-Inverted-On-fill-Hover);
|
||||
background:
|
||||
linear-gradient(
|
||||
0deg,
|
||||
var(--Component-Button-Inverted-Fill-Hover) 0%,
|
||||
var(--Component-Button-Inverted-Fill-Hover) 100%
|
||||
),
|
||||
var(--Component-Button-Inverted-Fill-Default);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ interface VideoPlayerProps extends VariantProps<typeof variants> {
|
||||
captions?: Caption[]
|
||||
focalPoint?: FocalPoint
|
||||
autoPlay?: boolean
|
||||
hasOverlay?: boolean
|
||||
}
|
||||
|
||||
export function VideoPlayer({
|
||||
@@ -31,6 +32,7 @@ export function VideoPlayer({
|
||||
className,
|
||||
variant = 'inline',
|
||||
autoPlay,
|
||||
hasOverlay,
|
||||
}: VideoPlayerProps) {
|
||||
const intl = useIntl()
|
||||
const videoRef = useRef<HTMLVideoElement>(null)
|
||||
@@ -84,7 +86,13 @@ export function VideoPlayer({
|
||||
const showMuteButton = variant === 'inline' && isActivated
|
||||
|
||||
return (
|
||||
<div className={cx(classNames, { [styles.isActivated]: isActivated })}>
|
||||
<div
|
||||
className={cx(
|
||||
classNames,
|
||||
{ [styles.isActivated]: isActivated },
|
||||
{ [styles.hasOverlay]: hasOverlay }
|
||||
)}
|
||||
>
|
||||
<video
|
||||
ref={videoRef}
|
||||
className={styles.video}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 0;
|
||||
|
||||
&.inline {
|
||||
border-radius: var(--Corner-radius-md);
|
||||
@@ -22,6 +23,18 @@
|
||||
right: var(--Space-x2);
|
||||
}
|
||||
}
|
||||
|
||||
&.hasOverlay::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: linear-gradient(
|
||||
180deg,
|
||||
rgba(255, 255, 255, 0) 0%,
|
||||
rgba(31, 28, 27, 0.25) 48.08%,
|
||||
rgba(31, 28, 27, 0.8) 100%
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
.video {
|
||||
@@ -32,17 +45,30 @@
|
||||
|
||||
.playButton {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.muteButton {
|
||||
position: absolute;
|
||||
top: var(--Space-x2);
|
||||
right: var(--Space-x2);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
.videoPlayer.hero .playButton {
|
||||
bottom: var(--Space-x4);
|
||||
right: var(--Space-x4);
|
||||
.videoPlayer {
|
||||
&.hero .playButton {
|
||||
bottom: var(--Space-x4);
|
||||
right: var(--Space-x4);
|
||||
}
|
||||
|
||||
&.hasOverlay::after {
|
||||
background: linear-gradient(
|
||||
180deg,
|
||||
rgba(255, 255, 255, 0) 0%,
|
||||
rgba(31, 28, 27, 0.25) 53.8%,
|
||||
rgba(31, 28, 27, 0.74) 97.6%
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,6 +188,7 @@
|
||||
"./Tooltip": "./lib/components/Tooltip/index.tsx",
|
||||
"./TripAdvisorChip": "./lib/components/TripAdvisorChip/index.tsx",
|
||||
"./Typography": "./lib/components/Typography/index.tsx",
|
||||
"./VideoPlayer": "./lib/components/VideoPlayer/index.tsx",
|
||||
"./base.css": "./lib/base.css",
|
||||
"./design-system-new-deprecated.css": "./lib/design-system-new-deprecated.css",
|
||||
"./downtown-camper.css": "./lib/styles/downtown-camper.css",
|
||||
|
||||
@@ -7,3 +7,10 @@ export const System = gql`
|
||||
uid
|
||||
}
|
||||
`
|
||||
|
||||
export const AssetSystem = gql`
|
||||
fragment AssetSystem on SysAssetSystemField {
|
||||
content_type_uid
|
||||
uid
|
||||
}
|
||||
`
|
||||
|
||||
45
packages/trpc/lib/graphql/Fragments/Video.graphql.ts
Normal file
45
packages/trpc/lib/graphql/Fragments/Video.graphql.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { gql } from "graphql-tag"
|
||||
|
||||
import { AssetSystem } from "./System.graphql"
|
||||
|
||||
export const Video = gql`
|
||||
fragment Video on Video {
|
||||
sourceConnection {
|
||||
edges {
|
||||
node {
|
||||
url
|
||||
}
|
||||
}
|
||||
}
|
||||
focal_point {
|
||||
x
|
||||
y
|
||||
}
|
||||
captions {
|
||||
is_default
|
||||
language
|
||||
fileConnection {
|
||||
edges {
|
||||
node {
|
||||
url
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const VideoRef = gql`
|
||||
fragment VideoRef on Video {
|
||||
sourceConnection {
|
||||
edges {
|
||||
node {
|
||||
system {
|
||||
...AssetSystem
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
${AssetSystem}
|
||||
`
|
||||
@@ -25,11 +25,15 @@ import {
|
||||
TopPrimaryButtonRef_CollectionPage,
|
||||
} from "../../Fragments/CollectionPage/TopPrimaryButton.graphql"
|
||||
import { System } from "../../Fragments/System.graphql"
|
||||
import { Video, VideoRef } from "../../Fragments/Video.graphql"
|
||||
|
||||
export const GetCollectionPage = gql`
|
||||
query GetCollectionPage($locale: String!, $uid: String!) {
|
||||
collection_page(uid: $uid, locale: $locale) {
|
||||
hero_image
|
||||
hero_video {
|
||||
...Video
|
||||
}
|
||||
title
|
||||
header {
|
||||
heading
|
||||
@@ -64,11 +68,15 @@ export const GetCollectionPage = gql`
|
||||
${Shortcuts_CollectionPage}
|
||||
${UspGrid_CollectionPage}
|
||||
${DynamicContent_CollectionPage}
|
||||
${Video}
|
||||
`
|
||||
|
||||
export const GetCollectionPageRefs = gql`
|
||||
query GetCollectionPageRefs($locale: String!, $uid: String!) {
|
||||
collection_page(locale: $locale, uid: $uid) {
|
||||
hero_video {
|
||||
...VideoRef
|
||||
}
|
||||
header {
|
||||
...TopPrimaryButtonRef_CollectionPage
|
||||
...NavigationLinksRef_CollectionPage
|
||||
@@ -92,6 +100,7 @@ export const GetCollectionPageRefs = gql`
|
||||
${Shortcuts_CollectionPageRefs}
|
||||
${UspGrid_CollectionPageRefs}
|
||||
${DynamicContent_CollectionPageRefs}
|
||||
${VideoRef}
|
||||
`
|
||||
|
||||
export const GetDaDeEnUrlsCollectionPage = gql`
|
||||
|
||||
@@ -60,11 +60,15 @@ import {
|
||||
TeaserCardSidebar_ContentPageRefs,
|
||||
} from "../../Fragments/Sidebar/TeaserCard.graphql"
|
||||
import { System } from "../../Fragments/System.graphql"
|
||||
import { Video, VideoRef } from "../../Fragments/Video.graphql"
|
||||
|
||||
export const GetContentPage = gql`
|
||||
query GetContentPage($locale: String!, $uid: String!) {
|
||||
content_page(uid: $uid, locale: $locale) {
|
||||
hero_image
|
||||
hero_video {
|
||||
...Video
|
||||
}
|
||||
title
|
||||
header {
|
||||
heading
|
||||
@@ -110,6 +114,7 @@ export const GetContentPage = gql`
|
||||
${ScriptedCardSidebar_ContentPage}
|
||||
${TeaserCardSidebar_ContentPage}
|
||||
${QuickLinksSidebar_ContentPage}
|
||||
${Video}
|
||||
`
|
||||
|
||||
export const GetContentPageBlocksBatch1 = gql`
|
||||
@@ -153,6 +158,9 @@ export const GetContentPageBlocksBatch2 = gql`
|
||||
export const GetContentPageRefs = gql`
|
||||
query GetContentPageRefs($locale: String!, $uid: String!) {
|
||||
content_page(locale: $locale, uid: $uid) {
|
||||
hero_video {
|
||||
...VideoRef
|
||||
}
|
||||
header {
|
||||
dynamic_content {
|
||||
component
|
||||
@@ -181,6 +189,7 @@ export const GetContentPageRefs = gql`
|
||||
${ScriptedCardSidebar_ContentPageRefs}
|
||||
${TeaserCardSidebar_ContentPageRefs}
|
||||
${QuickLinksSidebar_ContentPageRefs}
|
||||
${VideoRef}
|
||||
`
|
||||
|
||||
export const GetContentPageBlocksRefs = gql`
|
||||
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
} from "../schemas/linkConnection"
|
||||
import { internalOrExternalLinkSchema } from "../schemas/pageLinks"
|
||||
import { systemSchema } from "../schemas/system"
|
||||
import { transformedVideoSchema, videoRefSchema } from "../schemas/video"
|
||||
|
||||
// Block schemas
|
||||
export const collectionPageCards = z
|
||||
@@ -78,6 +79,7 @@ const navigationLinksSchema = z
|
||||
export const collectionPageSchema = z.object({
|
||||
collection_page: z.object({
|
||||
hero_image: transformedImageVaultAssetSchema,
|
||||
hero_video: transformedVideoSchema,
|
||||
blocks: discriminatedUnionArray(blocksSchema.options).nullable(),
|
||||
title: z.string(),
|
||||
header: z.object({
|
||||
@@ -145,6 +147,7 @@ const collectionPageHeaderRefs = z.object({
|
||||
|
||||
export const collectionPageRefsSchema = z.object({
|
||||
collection_page: z.object({
|
||||
hero_video: videoRefSchema.nullish(),
|
||||
header: collectionPageHeaderRefs,
|
||||
blocks: discriminatedUnionArray(
|
||||
collectionPageBlockRefsItem.options
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
import {
|
||||
generateRefsResponseTag,
|
||||
generateTag,
|
||||
generateTagsFromAssetSystem,
|
||||
generateTagsFromSystem,
|
||||
} from "../../../utils/generateTag"
|
||||
import { collectionPageRefsSchema } from "./output"
|
||||
@@ -84,6 +85,7 @@ export function generatePageTags(
|
||||
const connections = getConnections(validatedData)
|
||||
return [
|
||||
generateTagsFromSystem(lang, connections),
|
||||
generateTagsFromAssetSystem(connections),
|
||||
generateTag(lang, validatedData.collection_page.system.uid),
|
||||
].flat()
|
||||
}
|
||||
|
||||
@@ -59,6 +59,7 @@ import {
|
||||
teaserCardsSchema,
|
||||
} from "../schemas/sidebar/teaserCard"
|
||||
import { systemSchema } from "../schemas/system"
|
||||
import { transformedVideoSchema, videoRefSchema } from "../schemas/video"
|
||||
|
||||
// Block schemas
|
||||
export const contentPageCards = z
|
||||
@@ -194,6 +195,7 @@ const navigationLinksSchema = z
|
||||
export const contentPageSchema = z.object({
|
||||
content_page: z.object({
|
||||
hero_image: transformedImageVaultAssetSchema,
|
||||
hero_video: transformedVideoSchema,
|
||||
blocks: discriminatedUnionArray(blocksSchema.options).nullable(),
|
||||
sidebar: discriminatedUnionArray(sidebarSchema.options).nullable(),
|
||||
title: z.string(),
|
||||
@@ -323,6 +325,7 @@ const contentPageHeaderRefs = z.object({
|
||||
|
||||
export const contentPageRefsSchema = z.object({
|
||||
content_page: z.object({
|
||||
hero_video: videoRefSchema.nullish(),
|
||||
header: contentPageHeaderRefs,
|
||||
blocks: discriminatedUnionArray(
|
||||
contentPageBlockRefsItem.options
|
||||
|
||||
@@ -10,6 +10,7 @@ import { ContentPageEnum } from "../../../types/contentPage"
|
||||
import {
|
||||
generateRefsResponseTag,
|
||||
generateTag,
|
||||
generateTagsFromAssetSystem,
|
||||
generateTagsFromSystem,
|
||||
} from "../../../utils/generateTag"
|
||||
import { contentPageRefsSchema } from "./output"
|
||||
@@ -75,12 +76,14 @@ export function generatePageTags(
|
||||
const connections = getConnections(validatedData)
|
||||
return [
|
||||
generateTagsFromSystem(lang, connections),
|
||||
generateTagsFromAssetSystem(connections),
|
||||
generateTag(lang, validatedData.content_page.system.uid),
|
||||
].flat()
|
||||
}
|
||||
|
||||
export function getConnections({ content_page }: ContentPageRefs) {
|
||||
const connections: System["system"][] = [content_page.system]
|
||||
|
||||
if (content_page.blocks) {
|
||||
content_page.blocks.forEach((block) => {
|
||||
switch (block.__typename) {
|
||||
|
||||
@@ -11,3 +11,12 @@ export const systemSchema = z.object({
|
||||
export interface System {
|
||||
system: z.output<typeof systemSchema>
|
||||
}
|
||||
|
||||
export const assetSystemSchema = z.object({
|
||||
content_type_uid: z.string(),
|
||||
uid: z.string(),
|
||||
})
|
||||
|
||||
export interface AssetSystem {
|
||||
system: z.output<typeof assetSystemSchema>
|
||||
}
|
||||
|
||||
75
packages/trpc/lib/routers/contentstack/schemas/video.ts
Normal file
75
packages/trpc/lib/routers/contentstack/schemas/video.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
import { z } from "zod"
|
||||
|
||||
import { Lang } from "@scandic-hotels/common/constants/language"
|
||||
import { focalPointSchema } from "@scandic-hotels/common/utils/focalPoint"
|
||||
|
||||
import { assetSystemSchema } from "./system"
|
||||
|
||||
export const videoSchema = z.object({
|
||||
sourceConnection: z.object({
|
||||
edges: z.array(
|
||||
z.object({
|
||||
node: z.object({
|
||||
url: z.string().url(),
|
||||
}),
|
||||
})
|
||||
),
|
||||
}),
|
||||
focal_point: focalPointSchema.nullish(),
|
||||
captions: z.array(
|
||||
z.object({
|
||||
is_default: z.boolean(),
|
||||
fileConnection: z.object({
|
||||
edges: z.array(
|
||||
z.object({
|
||||
node: z.object({
|
||||
url: z.string().url(),
|
||||
}),
|
||||
})
|
||||
),
|
||||
}),
|
||||
language: z.nativeEnum(Lang),
|
||||
})
|
||||
),
|
||||
})
|
||||
|
||||
export const transformedVideoSchema = videoSchema
|
||||
.nullish()
|
||||
.transform((video) => {
|
||||
const src = video?.sourceConnection.edges[0]?.node.url || ""
|
||||
|
||||
if (!video || !src) {
|
||||
return null
|
||||
}
|
||||
|
||||
const captions = video.captions
|
||||
.filter((caption) => caption.fileConnection.edges[0]?.node.url)
|
||||
.map((caption) => ({
|
||||
src: caption.fileConnection.edges[0]?.node.url || "",
|
||||
srcLang: caption.language,
|
||||
isDefault: caption.is_default,
|
||||
}))
|
||||
|
||||
return {
|
||||
src,
|
||||
focalPoint: video.focal_point
|
||||
? { x: video.focal_point.x, y: video.focal_point.y }
|
||||
: { x: 50, y: 50 },
|
||||
captions,
|
||||
}
|
||||
})
|
||||
|
||||
export const videoRefSchema = z.object({
|
||||
sourceConnection: z.object({
|
||||
edges: z.array(
|
||||
z.object({
|
||||
node: z.object({
|
||||
system: assetSystemSchema,
|
||||
}),
|
||||
})
|
||||
),
|
||||
}),
|
||||
})
|
||||
|
||||
export type Video = z.output<typeof transformedVideoSchema>
|
||||
export type VideoCaptions = NonNullable<Video>["captions"]
|
||||
@@ -1,6 +1,9 @@
|
||||
import type { Lang } from "@scandic-hotels/common/constants/language"
|
||||
import { Lang } from "@scandic-hotels/common/constants/language"
|
||||
|
||||
import type { System } from "../routers/contentstack/schemas/system"
|
||||
import type {
|
||||
AssetSystem,
|
||||
System,
|
||||
} from "../routers/contentstack/schemas/system"
|
||||
import type { Edges } from "../types/edges"
|
||||
import type { NodeRefs } from "../types/refs"
|
||||
|
||||
@@ -85,6 +88,14 @@ export function generateTagsFromSystem(
|
||||
})
|
||||
}
|
||||
|
||||
export function generateTagsFromAssetSystem(
|
||||
connections: AssetSystem["system"][]
|
||||
) {
|
||||
return connections.map((system) => {
|
||||
return generateTag(Lang.en, system.content_type_uid, system.uid)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to generate tags for loyalty configuration models
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user