forked from adobe/aem-boilerplate
-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'aem-assets-plugin' of github.com:hlxsites/franklin-asse…
…ts-selector into aem-assets-plugin
- Loading branch information
Showing
20 changed files
with
1,526 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
# Using images from external URLs in AEM Franklin pages | ||
|
||
## Introduction | ||
This document explains a mechanism for getting images served from external URLs on AEM Franklin pages. You may find this useful if you want to have your images served from an external assets repository. | ||
|
||
## Process | ||
During the page authoring process, the author has to specify the external URL from which the image is served. This is done by placing external image links containing the hyperlinked publicly accessible image URLs on the Word/Google Document. The image links are then replaced with the actual images during the page rendering process. | ||
|
||
### Note for site authors | ||
Here's [an example page and document](https://ext-images--franklin-assets-selector--hlxsites.hlx.page/external-images-example?view-doc-source=true) that shows how to use external images in AEM Franklin pages. | ||
You can specify external images by just copying and pasting the image URL in the Word/Google Document. The image URL must be hyperlinked. If the hyperlink has an image file extension, it will be treated as an external image. If the hyperlink does not have an image file extension, it will be treated as a regular hyperlink. | ||
Alternatively, you can also explicitly specify an external image marker and adding the external image url as a hyperlink in it. | ||
The above [example page](https://ext-images--franklin-assets-selector--hlxsites.hlx.page/external-images-example?view-doc-source=true) demonstrates both the approaches. | ||
|
||
### Note for site developers | ||
Anchor tags get treated as external images if their `textContent` is same as their `href` attribute and the URL specified in `href` is of an image file extension. For e.g. `'jpg', 'jpeg', 'png', 'gif', 'webp'`. Web optimized image formats such as `webp` should be preferred. You can find the implementation of this [here](https://github.com/hlxsites/franklin-assets-selector/blob/9145aeac55512ec199152065b16db6c24cea3421/scripts/scripts.js#L105-L110) | ||
|
||
Alternatively, an *image marker* text can be used to explicitly indicate external images. This is a pre-configured value. You can configure it [here](https://github.com/hlxsites/franklin-assets-selector/blob/9145aeac55512ec199152065b16db6c24cea3421/scripts/scripts.js#L227). | ||
|
||
|
||
Also note that for creating optimized `picture` tags for external images, you must override `createOptimizedPicture` function. You can find a sample overidden implementation of `createOptimizedPicture` [here](https://github.com/hlxsites/franklin-assets-selector/blob/9145aeac55512ec199152065b16db6c24cea3421/scripts/scripts.js#L142-L182). | ||
|
||
To summarize, most of the logic for this [is here](https://github.com/hlxsites/franklin-assets-selector/blob/9145aeac55512ec199152065b16db6c24cea3421/scripts/scripts.js#L69-L218) and trigger point for it starts with `decorateExternalImages`. For e.g. [here with an implict external image decoration](https://github.com/hlxsites/franklin-assets-selector/blob/9145aeac55512ec199152065b16db6c24cea3421/scripts/scripts.js#L229-L230). | ||
|
||
``` | ||
export function decorateMain(main) { | ||
// decorate external images with implicit external image detection | ||
decorateExternalImages(main); | ||
... | ||
} | ||
``` | ||
|
||
And [here with an explicit external image marker](https://github.com/hlxsites/franklin-assets-selector/blob/9145aeac55512ec199152065b16db6c24cea3421/scripts/scripts.js#L226-L227). | ||
``` | ||
export function decorateMain(main) { | ||
// decorate external images with explicit external image marker | ||
decorateExternalImages(main, '//External Image//'); | ||
... | ||
} | ||
``` | ||
|
||
## How does this work? | ||
During the page rendering process, the frontend code replaces the anchor tags identified as exteernal images on the page with the `picture` tags with `src`/`srcset` attributes set as the external image's url as specified in the external image link placed on the Word / Google Document during the page authoring process. | ||
|
||
Authors can optionally specify query paramaters in the hyperlinked external url and they would be retained in the `picture` tag's `src`/`srcset` attributes. These are useful for specifying image delivery parameters such as image width, height, format, etc. as understood by the external image delivery service. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
main .embed { | ||
width: unset; | ||
text-align: center; | ||
max-width: 800px; | ||
margin: 32px auto; | ||
} | ||
|
||
main .embed > div { | ||
display: flex; | ||
justify-content: center; | ||
} | ||
|
||
main .embed.embed-twitter .twitter-tweet-rendered { | ||
margin-left: auto; | ||
margin-right: auto; | ||
} | ||
|
||
main .embed .embed-placeholder { | ||
width: 100%; | ||
aspect-ratio: 16 / 9; | ||
position: relative; | ||
} | ||
|
||
main .embed .embed-placeholder > * { | ||
display: flex; | ||
align-items: center; | ||
justify-content: center; | ||
position: absolute; | ||
inset: 0; | ||
} | ||
|
||
main .embed .embed-placeholder picture img { | ||
width: 100%; | ||
height: 100%; | ||
object-fit: cover; | ||
} | ||
|
||
main .embed .embed-placeholder-play button { | ||
box-sizing: border-box; | ||
position: relative; | ||
display: block; | ||
transform: scale(3); | ||
width: 22px; | ||
height: 22px; | ||
border: 2px solid; | ||
border-radius: 20px; | ||
padding: 0; | ||
} | ||
|
||
main .embed .embed-placeholder-play button::before { | ||
content: ""; | ||
display: block; | ||
box-sizing: border-box; | ||
position: absolute; | ||
width: 0; | ||
height: 10px; | ||
border-top: 5px solid transparent; | ||
border-bottom: 5px solid transparent; | ||
border-left: 6px solid; | ||
top: 4px; | ||
left: 7px; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
/* | ||
* Embed Block | ||
* Show videos and social posts directly on your page | ||
* https://www.hlx.live/developer/block-collection/embed | ||
*/ | ||
|
||
const loadScript = (url, callback, type) => { | ||
const head = document.querySelector('head'); | ||
const script = document.createElement('script'); | ||
script.src = url; | ||
if (type) { | ||
script.setAttribute('type', type); | ||
} | ||
script.onload = callback; | ||
head.append(script); | ||
return script; | ||
}; | ||
|
||
const getDefaultEmbed = (url) => `<div style="left: 0; width: 100%; height: 0; position: relative; padding-bottom: 56.25%;"> | ||
<iframe src="${url.href}" style="border: 0; top: 0; left: 0; width: 100%; height: 100%; position: absolute;" allowfullscreen="" | ||
scrolling="no" allow="encrypted-media" title="Content from ${url.hostname}" loading="lazy"> | ||
</iframe> | ||
</div>`; | ||
|
||
const embedYoutube = (url, autoplay) => { | ||
const usp = new URLSearchParams(url.search); | ||
const suffix = autoplay ? '&muted=1&autoplay=1' : ''; | ||
let vid = usp.get('v') ? encodeURIComponent(usp.get('v')) : ''; | ||
const embed = url.pathname; | ||
if (url.origin.includes('youtu.be')) { | ||
[, vid] = url.pathname.split('/'); | ||
} | ||
const embedHTML = `<div style="left: 0; width: 100%; height: 0; position: relative; padding-bottom: 56.25%;"> | ||
<iframe src="https://www.youtube.com${vid ? `/embed/${vid}?rel=0&v=${vid}${suffix}` : embed}" style="border: 0; top: 0; left: 0; width: 100%; height: 100%; position: absolute;" | ||
allow="autoplay; fullscreen; picture-in-picture; encrypted-media; accelerometer; gyroscope; picture-in-picture" allowfullscreen="" scrolling="no" title="Content from Youtube" loading="lazy"></iframe> | ||
</div>`; | ||
return embedHTML; | ||
}; | ||
|
||
const embedVimeo = (url, autoplay) => { | ||
const [, video] = url.pathname.split('/'); | ||
const suffix = autoplay ? '?muted=1&autoplay=1' : ''; | ||
const embedHTML = `<div style="left: 0; width: 100%; height: 0; position: relative; padding-bottom: 56.25%;"> | ||
<iframe src="https://player.vimeo.com/video/${video}${suffix}" | ||
style="border: 0; top: 0; left: 0; width: 100%; height: 100%; position: absolute;" | ||
frameborder="0" allow="autoplay; fullscreen; picture-in-picture" allowfullscreen | ||
title="Content from Vimeo" loading="lazy"></iframe> | ||
</div>`; | ||
return embedHTML; | ||
}; | ||
|
||
const embedTwitter = (url) => { | ||
const embedHTML = `<blockquote class="twitter-tweet"><a href="${url.href}"></a></blockquote>`; | ||
loadScript('https://platform.twitter.com/widgets.js'); | ||
return embedHTML; | ||
}; | ||
|
||
const loadEmbed = (block, link, autoplay) => { | ||
if (block.classList.contains('embed-is-loaded')) { | ||
return; | ||
} | ||
|
||
const EMBEDS_CONFIG = [ | ||
{ | ||
match: ['youtube', 'youtu.be'], | ||
embed: embedYoutube, | ||
}, | ||
{ | ||
match: ['vimeo'], | ||
embed: embedVimeo, | ||
}, | ||
{ | ||
match: ['twitter'], | ||
embed: embedTwitter, | ||
}, | ||
]; | ||
|
||
const config = EMBEDS_CONFIG.find((e) => e.match.some((match) => link.includes(match))); | ||
const url = new URL(link); | ||
if (config) { | ||
block.innerHTML = config.embed(url, autoplay); | ||
block.classList = `block embed embed-${config.match[0]}`; | ||
} else { | ||
block.innerHTML = getDefaultEmbed(url); | ||
block.classList = 'block embed'; | ||
} | ||
block.classList.add('embed-is-loaded'); | ||
}; | ||
|
||
export default function decorate(block) { | ||
const placeholder = block.querySelector('picture'); | ||
const link = block.querySelector('a').href; | ||
block.textContent = ''; | ||
|
||
if (placeholder) { | ||
const wrapper = document.createElement('div'); | ||
wrapper.className = 'embed-placeholder'; | ||
wrapper.innerHTML = '<div class="embed-placeholder-play"><button title="Play"></button></div>'; | ||
wrapper.prepend(placeholder); | ||
wrapper.addEventListener('click', () => { | ||
loadEmbed(block, link, true); | ||
}); | ||
block.append(wrapper); | ||
} else { | ||
const observer = new IntersectionObserver((entries) => { | ||
if (entries.some((e) => e.isIntersecting)) { | ||
observer.disconnect(); | ||
loadEmbed(block, link); | ||
} | ||
}); | ||
observer.observe(block); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
/* CSS: styles.css */ | ||
.block.hotspot { | ||
position: relative; | ||
background: none; | ||
} | ||
|
||
.block.hotspot img { | ||
max-width: 100%; | ||
width: auto; | ||
height: auto; | ||
} | ||
|
||
.block.hotspot > div.hotspot { | ||
background: red; /* Temporary for visibility */ | ||
z-index: 10; /* Ensure it is above the image */ | ||
width: 20px; | ||
height: 20px; | ||
border-radius: 100%; | ||
border: 3px solid white; | ||
position: absolute; | ||
cursor: pointer; | ||
} | ||
|
||
.block.hotspot img, | ||
.block.hotspot video { | ||
max-width: 100%; | ||
width: auto; | ||
height: auto; | ||
} | ||
|
||
.block.hotspot > div.hotspot .hotspot-content { | ||
display: none; /* Hidden by default */ | ||
position: absolute; | ||
top: -10px; /* Adjust positioning as needed */ | ||
left: 50%; | ||
transform: translateX(-50%); | ||
padding: 10px; | ||
border-radius: 5px; | ||
z-index: 15; | ||
width: 200px; /* Adjust size as needed */ | ||
} | ||
|
||
.block.hotspot > div.hotspot .hotspot-content.bgborder { | ||
background: white; | ||
border: 1px solid white; | ||
} | ||
|
||
.block.hotspot > div.hotspot.onclick .hotspot-content, | ||
.block.hotspot > div.hotspot:hover .hotspot-content { | ||
display: block; /* Show on click or hover */ | ||
} | ||
|
||
.block.hotspot > div.hotspot .hotspot-content img, | ||
.block.hotspot > div.hotspot .hotspot-content video { | ||
width: 400px; | ||
height: auto; | ||
max-width: unset; | ||
} | ||
|
||
.block.hotspot > div.hotspot .hotspot-content video { | ||
max-height: 150px; /* Adjust as needed */ | ||
} | ||
|
||
.block.hotspot > div.hotspot:hover::after, | ||
.block.hotspot > div.hotspot.onclick::after { | ||
opacity: 1; | ||
} | ||
|
||
.block.hotspot svg { | ||
position: absolute; | ||
top: 0; | ||
left: 0; | ||
z-index: 0; | ||
} |
Oops, something went wrong.