diff --git a/.changeset/sad-pumas-design.md b/.changeset/sad-pumas-design.md new file mode 100644 index 0000000..91a361a --- /dev/null +++ b/.changeset/sad-pumas-design.md @@ -0,0 +1,5 @@ +--- +"@stackla/widget-utils": patch +--- + +Add expanded tile variant type diff --git a/src/libs/vertical-expanded-tiles/base.template.tsx b/src/libs/vertical-expanded-tiles/base.template.tsx new file mode 100644 index 0000000..68897de --- /dev/null +++ b/src/libs/vertical-expanded-tiles/base.template.tsx @@ -0,0 +1,54 @@ +import { ISdk, Tile } from "src/types" +import { VerticalExpandedTile } from "./tile.template" +import { createElement } from "../jsx-html" + +export function getExpandedSlides(sdk: ISdk, tiles: Tile[]) { + return tiles.map(tile => ( +
+ )) +} + +export function VerticalExpandedTiles(sdk: ISdk) { + const tiles = sdk.getTiles() + const { show_nav } = sdk.getExpandedTileConfig() + const navigationArrowsEnabled = show_nav + + return ( + + ) +} diff --git a/src/libs/vertical-expanded-tiles/config.ts b/src/libs/vertical-expanded-tiles/config.ts new file mode 100644 index 0000000..23a8038 --- /dev/null +++ b/src/libs/vertical-expanded-tiles/config.ts @@ -0,0 +1,27 @@ +import { MyWidgetSettings } from "src/types" +import { VerticalExpandedTiles } from "./base.template" +import ProductsTemplate from "./products.template" +import { InlineProductsTemplate } from "./inline-products.template" +import { TileContentTemplate } from "./tile-content.template" + +export const loadVerticalExpandedTilesConfig = (config: MyWidgetSettings = {}) => { + config.templates = { + ...config.templates + } + config.templates["expanded-tiles"] = VerticalExpandedTiles + config.templates["ugc-product"] = ProductsTemplate + config.templates["inline-products"] = InlineProductsTemplate + config.templates["tile-content"] = TileContentTemplate + config.config = { + ...config.config, + expandedTile: { + ...config.config?.expandedTile, + swiper_options: { + ...config.config?.expandedTile?.swiper_options, + direction: "vertical" + } + } + } + + return config +} diff --git a/src/libs/vertical-expanded-tiles/embed-youtube.template.tsx b/src/libs/vertical-expanded-tiles/embed-youtube.template.tsx new file mode 100644 index 0000000..deb2ec1 --- /dev/null +++ b/src/libs/vertical-expanded-tiles/embed-youtube.template.tsx @@ -0,0 +1,131 @@ +import { createElement } from "../../" + +export type EmbedYoutubeProps = { + tileId: string + videoId: string + onLoad?: (event: Event) => void + swiperId: string +} + +export function EmbedYoutube({ tileId, videoId, onLoad, swiperId }: EmbedYoutubeProps) { + const contentElement = loadYoutubeIframeContent(tileId, videoId, swiperId) + + return ( + + ) +} + +function loadYoutubeIframeContent(tileId: string, videoId: string, swiperId: string) { + const scriptId = `yt-script-${tileId}-${videoId}` + const playerId = `yt-player-${tileId}-${videoId}` + return ( + + + + + + + + + + + ) +} + +export function loadYoutubePlayerAPI(playerId: string, videoId: string, swiperId: string) { + return ` + let player; + let swiper = parent.window.ugc.swiperContainer["${swiperId}"]; + const instance = swiper?.instance; + + function onPlayerStateChange(event) { + instance?.autoplay?.stop(); + if (event.data === YT.PlayerState.ENDED) { + instance?.autoplay?.start(); + instance?.slideNext(); + } + } + + function loadPlayer(playDefault = false) { + player = new YT.Player("${playerId}", { + width: "100%", + height: "100%", + videoId: "${videoId}", + playerVars: { + autoplay: 0, + controls: 1, + modestbranding: 1, + rel: 0, + enablejsapi: 1, + playsinline: 1, + }, + events: { + onReady: playDefault ? play : pause, + onStateChange: onPlayerStateChange, + onError: errorHandler + } + }); + } + + function onYouTubeIframeAPIReady() { + loadPlayer(); + } + + function errorHandler(e) { + player?.getIframe().dispatchEvent(new CustomEvent("yt-video-error", { detail: e })); + } + + function pause() { + if (player && player.pauseVideo) { + player.pauseVideo(); + } + } + + function play() { + if (player && player.playVideo) { + player.playVideo(); + } + } + + function observeVisibility() { + const observer = new IntersectionObserver((entries) => { + entries.forEach(entry => { + if (entry.isIntersecting) { + play(); + } else { + pause(); + instance?.autoplay?.start(); + } + }); + }, { threshold: 0.5 }); + + observer.observe(document.body); + } + + window.onYouTubeIframeAPIReady = () => { + loadPlayer(false); + observeVisibility(); + }; + ` +} diff --git a/src/libs/vertical-expanded-tiles/expanded-swiper.loader.tsx b/src/libs/vertical-expanded-tiles/expanded-swiper.loader.tsx new file mode 100644 index 0000000..3448907 --- /dev/null +++ b/src/libs/vertical-expanded-tiles/expanded-swiper.loader.tsx @@ -0,0 +1,558 @@ +import { + EVENT_EXPANDED_TILE_CLOSE, + EVENT_LOAD_MORE, + EVENT_TILE_EXPAND_RENDERED, + ISdk, + registerDefaultClickEvents, + registerGenericEventListener, + registerProductsUpdatedListener, + registerShareMenuClosedListener, + registerShareMenuOpenedListener, + registerTileExpandListener +} from "../../" + +import { + destroySwiper, + getActiveSlide, + getActiveSlideElement, + getSwiperIndexForTile, + initializeSwiper, + LookupAttr, + SwiperWithExtensions, + updateSwiperInstance +} from "../extensions" + +import { loadProductsSwiper } from "./products.swiper" +import { waitForElm } from "../../libs" +import { type SwiperType as Swiper } from "../../types" +import { muteTiktokVideo, unMuteTiktokVideo } from "./tiktok-message" +import { getExpandedSlides } from "./base.template" +import { + controlVideoPlayback, + setupTikTokPlayerReadyEvent, + setupVideoEvents, + setupYoutubeEvents, + YoutubeIframeElementType +} from "./expanded-tile-video" +import { SwiperTypeOptions } from "../../types" + +interface ExpandedTileSettings { + initialTileId: string + lookupAttr?: LookupAttr + widgetSelector: HTMLElement + expandedTileWrapper: Element + swiperSettings?: SwiperTypeOptions +} + +/** + * Initialize/re-initialize swiper for loading expanded tiles + * + * @param { string } initialTileId - id of the tile that should be displayed after loading swiper + * @param { LookupAttr } lookupAttr - additional attribute lookup options for finding the first slide to load + * @param { LookupAttr.name } lookupAttr.name - name of the attribute for e.g. data-yt-id or data-tiktok-id + * @param { LookupAttr.value } lookupAttr.value - required value of the attribute + */ +function initializeSwiperForExpandedTiles( + sdk: ISdk, + paritalSettings: Partial