Skip to content
Open
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
4 changes: 2 additions & 2 deletions docs/admin-preview.html
Original file line number Diff line number Diff line change
Expand Up @@ -139,11 +139,11 @@
55.627 l 55.6165,55.627 -231.245496,231.24803 c -127.185,127.1864
-231.5279,231.248 -231.873,231.248 -0.3451,0 -104.688,
-104.0616 -231.873,-231.248 z
" fill="currentColor"></path></g></svg></div></div><div class="sc-fkYqBV buanwU api-content"><div class="sc-dTvVRJ bPmFpz"><div class="sc-jJLAfE gkiSyE"><div class="sc-ggWZvA fqkwbU api-info"><h1 class="sc-hwkwBN sc-jCWzJg wYHiz hPcPCj">AEM Admin API (Feature Preview)<!-- --> <span>(<!-- -->12.100.18<!-- -->)</span></h1><p>Download OpenAPI specification<!-- -->:</p><div class="sc-eVqvcJ sc-fszimp kIppRw kbZred"><div class="sc-erPUmh eAqtbt"><div class="sc-iRTMaw beOrEi"> <!-- --> <span class="sc-jVxTAy hijBKj">License:<!-- --> <a href="https://www.apache.org/licenses/LICENSE-2.0.html">Apache 2.0</a></span> </div></div></div><div data-role="redoc-summary" html="" class="sc-eVqvcJ sc-fszimp kIppRw kbZred"></div><div data-role="redoc-description" html="&lt;p&gt;AEM Admin API is used to manage the lifecycle of content and code.&lt;/p&gt;
" fill="currentColor"></path></g></svg></div></div><div class="sc-fkYqBV buanwU api-content"><div class="sc-dTvVRJ bPmFpz"><div class="sc-jJLAfE gkiSyE"><div class="sc-ggWZvA fqkwbU api-info"><h1 class="sc-hwkwBN sc-jCWzJg wYHiz hPcPCj">AEM Admin API (Feature Preview)<!-- --> <span>(<!-- -->12.100.19<!-- -->)</span></h1><p>Download OpenAPI specification<!-- -->:</p><div class="sc-eVqvcJ sc-fszimp kIppRw kbZred"><div class="sc-erPUmh eAqtbt"><div class="sc-iRTMaw beOrEi"> <!-- --> <span class="sc-jVxTAy hijBKj">License:<!-- --> <a href="https://www.apache.org/licenses/LICENSE-2.0.html">Apache 2.0</a></span> </div></div></div><div data-role="redoc-summary" html="" class="sc-eVqvcJ sc-fszimp kIppRw kbZred"></div><div data-role="redoc-description" html="&lt;p&gt;AEM Admin API is used to manage the lifecycle of content and code.&lt;/p&gt;
" class="sc-eVqvcJ sc-fszimp kIppRw kbZred"><p>AEM Admin API is used to manage the lifecycle of content and code.</p>
</div></div></div></div></div><div class="sc-evkzZa iZqpqg"></div></div></div>
<script>
const __redoc_state = {"menu":{"activeItemIdx":-1},"spec":{"data":{"openapi":"3.1.0","info":{"version":"12.100.18","title":"AEM Admin API (Feature Preview)","description":"AEM Admin API is used to manage the lifecycle of content and code.","license":{"name":"Apache 2.0","url":"https://www.apache.org/licenses/LICENSE-2.0.html"}},"servers":[{"url":"https://admin.hlx.page"}],"components":{"securitySchemes":{"AuthCookie":{"type":"apiKey","in":"cookie","name":"auth_token"},"BearerToken":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}}}},"searchIndex":{"store":[],"index":{"version":"2.3.9","fields":["title","description"],"fieldVectors":[],"invertedIndex":[],"pipeline":[]}},"options":{"schemasExpansionLevel":2}};
const __redoc_state = {"menu":{"activeItemIdx":-1},"spec":{"data":{"openapi":"3.1.0","info":{"version":"12.100.19","title":"AEM Admin API (Feature Preview)","description":"AEM Admin API is used to manage the lifecycle of content and code.","license":{"name":"Apache 2.0","url":"https://www.apache.org/licenses/LICENSE-2.0.html"}},"servers":[{"url":"https://admin.hlx.page"}],"components":{"securitySchemes":{"AuthCookie":{"type":"apiKey","in":"cookie","name":"auth_token"},"BearerToken":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}}}},"searchIndex":{"store":[],"index":{"version":"2.3.9","fields":["title","description"],"fieldVectors":[],"invertedIndex":[],"pipeline":[]}},"options":{"schemasExpansionLevel":2}};

var container = document.getElementById('redoc');
Redoc.hydrate(__redoc_state, container);
Expand Down
4 changes: 2 additions & 2 deletions docs/admin.html

Large diffs are not rendered by default.

145 changes: 145 additions & 0 deletions scripts/scripts.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ import {

// Constants here
const LCP_BLOCKS = ['hero', 'logo-wall']; // add your LCP blocks to the list
const LONG_FORM_TEMPLATE_CLASSES = new Set(['docs-template', 'guides-template', 'blog-template', 'skills-template']);
const LONG_FORM_TEMPLATE_METADATA = new Set(['docs', 'documentation', 'guide', 'guides', 'blog', 'skills']);
const LONG_FORM_SCROLL_THRESHOLD = 0.35;

const AUDIENCES = {
mobile: () => window.innerWidth < 600,
Expand Down Expand Up @@ -183,6 +186,145 @@ export function addMessageBoxOnGuideTemplate(main) {
main.append(messageBox);
}

const isLongFormPage = () => {
const { body } = document;
if (!body) return false;
const hasTemplateClass = [...LONG_FORM_TEMPLATE_CLASSES]
.some((tplClass) => body.classList.contains(tplClass));
if (hasTemplateClass) return true;
const templateMetadata = getMetadata('template');
if (!templateMetadata) return false;
return LONG_FORM_TEMPLATE_METADATA.has(templateMetadata.toLowerCase());
};

const hasScrollableLongFormContent = () => {
const scrollable = document.documentElement.scrollHeight - window.innerHeight;
return scrollable > window.innerHeight * LONG_FORM_SCROLL_THRESHOLD;
};

function initReadingProgress() {
if (!isLongFormPage() || document.querySelector('.reading-progress')) return;

const progressBar = createTag('div', {
class: 'reading-progress',
role: 'progressbar',
'aria-label': 'Reading progress',
'aria-valuemin': '0',
'aria-valuemax': '100',
'aria-valuenow': '0',
'aria-hidden': 'true',
});
const fill = createTag('span', {
class: 'reading-progress-fill',
'aria-hidden': 'true',
});
progressBar.append(fill);
document.body.prepend(progressBar);

const updateProgress = () => {
const scrollable = Math.max(document.documentElement.scrollHeight - window.innerHeight, 0);
const canScroll = scrollable > 0;
const isLongEnough = hasScrollableLongFormContent();
const shouldHide = !canScroll || !isLongEnough;
progressBar.classList.toggle('is-hidden', shouldHide);
progressBar.setAttribute('aria-hidden', shouldHide ? 'true' : 'false');
if (shouldHide) {
fill.style.transform = 'scaleX(0)';
progressBar.setAttribute('aria-valuenow', '0');
return;
}
const progress = Math.min(window.scrollY / scrollable, 1);
fill.style.transform = `scaleX(${progress})`;
progressBar.setAttribute('aria-valuenow', `${Math.round(progress * 100)}`);
};

updateProgress();

let ticking = false;
const handleScroll = () => {
if (!ticking) {
window.requestAnimationFrame(() => {
updateProgress();
ticking = false;
});
ticking = true;
}
};

window.addEventListener('scroll', handleScroll, { passive: true });
window.addEventListener('resize', updateProgress);

if (window.ResizeObserver && document.body) {
const resizeObserver = new ResizeObserver(updateProgress);
resizeObserver.observe(document.body);
}
}

function initBackToTopButton() {
if (!isLongFormPage() || document.querySelector('.back-to-top-button')) return;

const button = createTag('button', {
type: 'button',
class: 'back-to-top-button',
'aria-label': 'Back to top',
});
const icon = createTag('span', {
class: 'back-to-top-button-icon',
'aria-hidden': 'true',
});
const label = createTag('span', { class: 'back-to-top-button-label' }, 'Back to top');
button.append(icon, label);
document.body.append(button);
button.setAttribute('aria-hidden', 'true');
button.setAttribute('tabindex', '-1');

const prefersReducedMotionQuery = window.matchMedia
? window.matchMedia('(prefers-reduced-motion: reduce)')
: null;
let prefersReducedMotion = prefersReducedMotionQuery?.matches;
const updateMotionPreference = (event) => {
prefersReducedMotion = event.matches;
};
if (prefersReducedMotionQuery?.addEventListener) {
prefersReducedMotionQuery.addEventListener('change', updateMotionPreference);
} else if (prefersReducedMotionQuery?.addListener) {
prefersReducedMotionQuery.addListener(updateMotionPreference);
}

const scrollToTop = () => {
window.scrollTo({
top: 0,
behavior: prefersReducedMotion ? 'auto' : 'smooth',
});
button.blur();
};

button.addEventListener('click', scrollToTop);

const toggleVisibility = () => {
const canScroll = hasScrollableLongFormContent();
if (!canScroll) {
button.classList.remove('is-visible');
button.setAttribute('aria-hidden', 'true');
button.setAttribute('tabindex', '-1');
return;
}
button.setAttribute('aria-hidden', 'false');
button.setAttribute('tabindex', '0');
const shouldShow = window.scrollY > window.innerHeight * 0.8;
button.classList.toggle('is-visible', shouldShow);
};

toggleVisibility();
window.addEventListener('scroll', toggleVisibility, { passive: true });
window.addEventListener('resize', toggleVisibility);

if (window.ResizeObserver && document.body) {
const resizeObserver = new ResizeObserver(toggleVisibility);
resizeObserver.observe(document.body);
}
}

export function addHeadingAnchorLink(elem) {
const link = document.createElement('a');
link.setAttribute('href', `#${elem.id || ''}`);
Expand Down Expand Up @@ -777,6 +919,9 @@ async function loadLazy(doc) {

loadCSS(`${window.hlx.codeBasePath}/styles/lazy-styles.css`);

initReadingProgress();
initBackToTopButton();

if (getMetadata('supressframe')) {
doc.querySelector('header').remove();
doc.querySelector('footer').remove();
Expand Down
109 changes: 109 additions & 0 deletions styles/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,115 @@ body.appear {
display: unset;
}

.reading-progress {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 3px;
background: rgb(104 112 236 / 15%);
z-index: 10000;
pointer-events: none;
overflow: hidden;
opacity: 1;
transition: opacity 0.2s var(--cubic-bezier-1);
}

.reading-progress.is-hidden {
opacity: 0;
}

.reading-progress-fill {
display: block;
width: 100%;
height: 100%;
background: linear-gradient(
90deg,
var(--color-accent-blue, var(--spectrum-blue)) 0%,
var(--dark-spectrum-blue)
);
transform-origin: left center;
transform: scaleX(0);
transition: transform 0.18s var(--cubic-bezier-1);
}

.back-to-top-button {
position: fixed;
bottom: var(--spacing-s);
right: var(--spacing-s);
display: inline-flex;
align-items: center;
gap: var(--spacing-xxs);
padding: 10px 16px;
border-radius: 999px;
background: var(--color-white);
border: 1px solid rgb(104 112 236 / 35%);
color: var(--color-font-grey);
font-size: var(--type-body-s-size);
font-weight: 600;
box-shadow: 0 18px 32px rgb(0 0 0 / 15%);
opacity: 0;
pointer-events: none;
transform: translateY(8px);
transition: opacity 0.25s var(--cubic-bezier-2),
transform 0.25s var(--cubic-bezier-2),
box-shadow 0.25s var(--cubic-bezier-2);
z-index: 10001;
}

.back-to-top-button.is-visible {
opacity: 1;
pointer-events: auto;
transform: translateY(0);
}

.back-to-top-button:focus-visible {
outline: 2px solid var(--color-accent-blue, var(--spectrum-blue));
outline-offset: 4px;
}

.back-to-top-button-icon {
width: 22px;
height: 22px;
border-radius: 50%;
background: var(--color-accent-blue, var(--spectrum-blue));
color: var(--color-white);
display: inline-flex;
align-items: center;
justify-content: center;
box-shadow: inset 0 0 0 1px rgb(255 255 255 / 30%);
}

.back-to-top-button-icon::before {
content: '';
width: 6px;
height: 6px;
border-left: 2px solid currentcolor;
border-top: 2px solid currentcolor;
transform: rotate(45deg);
margin-top: 2px;
}

.back-to-top-button-label {
white-space: nowrap;
}

@media screen and (width <= 600px) {
.back-to-top-button {
bottom: var(--spacing-xs);
right: var(--spacing-xs);
padding: 8px 12px;
}
}

@media (prefers-reduced-motion: reduce) {
.reading-progress-fill,
.reading-progress,
.back-to-top-button {
transition: none;
}
}

/* block style for container width, max-width */
.block.contained,
.section.content > div,
Expand Down
22 changes: 12 additions & 10 deletions tools/oversight/elements/list-facet.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,18 +105,20 @@ export default class ListFacet extends HTMLElement {
const facetEntries = this.dataChunks.facets[facetName];
const enabled = !this.closest('facet-sidebar[aria-disabled="true"]');

const sort = this.getAttribute('sort') || 'count';
const [sortMetric, sortProperty] = (this.getAttribute('sort') || 'weight').split('.');

const optionKeys = facetEntries.map((f) => f.value)
const optionKeys = facetEntries
.sort((a, b) => {
if (sort === 'count') return 0; // keep the order
// try to coerce into number (ignore prefixes)
const aNum = Number.parseFloat(a.replace(/[^0-9.]/g, ''));
const bNum = Number.parseFloat(b.replace(/[^0-9.]/g, ''));
// if not a number, just sort alphabetically
if (Number.isNaN(aNum) || Number.isNaN(bNum)) return a.localeCompare(b);
return aNum - bNum;
});
if (sortMetric === 'count') return 0; // keep the default order from distiller
if (sortMetric === 'weight') return b.weight - a.weight; // order by weight, aka number of page views
if (sortMetric === 'value') return a.value.localeCompare(b.value);
// get metric and property (e.g. lcp.mean)
const aNum = a.metrics[sortMetric][sortProperty];
const bNum = b.metrics[sortMetric][sortProperty];
return bNum - aNum;
})
// we only care about the value, not the full object
.map((f) => f.value);

const url = new URL(window.location);
const drilldownAtt = this.getAttribute('drilldown');
Expand Down
6 changes: 4 additions & 2 deletions tools/rum/elements/list-facet.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// eslint-disable-next-line import/no-unresolved
import { utils, stats } from '@adobe/rum-distiller';
import { stats, utils } from '@adobe/rum-distiller';
import {
escapeHTML, toHumanReadable,
} from '../utils.js';
Expand Down Expand Up @@ -109,7 +109,9 @@ export default class ListFacet extends HTMLElement {

const sort = this.getAttribute('sort') || 'count';

const optionKeys = facetEntries.map((f) => f.value)
const optionKeys = facetEntries
.sort(({ weight: a }, { weight: b }) => b - a) // sort by weight first
.map((f) => f.value)
.sort((a, b) => {
if (sort === 'count') return 0; // keep the order
return a.localeCompare(b);
Expand Down