Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/sad-pumas-design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@stackla/widget-utils": patch
---

Add expanded tile variant type
54 changes: 54 additions & 0 deletions src/libs/vertical-expanded-tiles/base.template.tsx
Original file line number Diff line number Diff line change
@@ -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 => (
<div
class="ugc-tile swiper-slide"
data-id={tile.id}
data-yt-id={tile.youtube_id || ""}
data-tiktok-id={tile.tiktok_id || ""}
data-media={tile.media}>
<VerticalExpandedTile tile={tile} sdk={sdk} />
</div>
))
}

export function VerticalExpandedTiles(sdk: ISdk) {
const tiles = sdk.getTiles()
const { show_nav } = sdk.getExpandedTileConfig()
const navigationArrowsEnabled = show_nav

return (
<div class="expanded-tile-wrapper" variation="vertical">
<div class="swiper swiper-expanded story-expanded">
<div class="align-center">
<div class="swiper-expanded-button-wrapper">
<div
class="swiper-expanded-button-prev swiper-button-prev btn-lg"
style={{ display: navigationArrowsEnabled ? "flex" : "none" }}>
<span class="chevron-left" alt="Previous arrow" />
</div>
<div
class="swiper-expanded-button-next swiper-button-next btn-lg"
style={{ display: navigationArrowsEnabled ? "flex" : "none" }}>
<span class="chevron-right" alt="Next arrow" />
</div>
</div>
</div>
<div class="swiper-wrapper ugc-tiles">
{tiles.map(tile => (
<div
class="ugc-tile swiper-slide"
data-id={tile.id}
data-yt-id={tile.youtube_id || ""}
data-tiktok-id={tile.tiktok_id || ""}>
<VerticalExpandedTile sdk={sdk} tile={tile} />
</div>
))}
</div>
</div>
</div>
)
}
27 changes: 27 additions & 0 deletions src/libs/vertical-expanded-tiles/config.ts
Original file line number Diff line number Diff line change
@@ -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
}
131 changes: 131 additions & 0 deletions src/libs/vertical-expanded-tiles/embed-youtube.template.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<iframe
loading="lazy"
id={`yt-frame-${tileId}-${videoId}`}
tileid={tileId}
class="video-content"
frameborder="0"
enablejsapi="1"
onload={onLoad}
srcdoc={contentElement.innerHTML}></iframe>
)
}

function loadYoutubeIframeContent(tileId: string, videoId: string, swiperId: string) {
const scriptId = `yt-script-${tileId}-${videoId}`
const playerId = `yt-player-${tileId}-${videoId}`
return (
<html>
<head>
<script id={scriptId} src="https://www.youtube.com/iframe_api"></script>
<script>{loadYoutubePlayerAPI(playerId, videoId, swiperId)}</script>
<style>{`
body {
margin: 0;
height: 100dvh;
scrollbar-width: none;
overflow: hidden;
iframe {
height: 100dvh;
width: 100%;
overflow: hidden;
scrollbar-width: none;
}
}
`}</style>
</head>
<body>
<div id={playerId}></div>
</body>
</html>
)
}

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();
};
`
}
Loading
Loading