mirror of
https://codeberg.org/simonrepp/hyper8.git
synced 2026-08-14 13:45:27 +02:00
Support toggling the playlist sidebar for a session, refactor script globals
This commit is contained in:
@@ -16,8 +16,6 @@ const siteTreeElements = siteTree.querySelector('.elements');
|
||||
const siteTreeStatus = siteTree.querySelector('[role="status"]');
|
||||
|
||||
const indexSuffix = window.location.pathname.endsWith('index.html') ? 'index.html' : '';
|
||||
const rootPrefix = document.querySelector('.home').dataset.rootPrefix;
|
||||
const sitePath = document.querySelector('.home').dataset.sitePath;
|
||||
|
||||
// We only render the site tree elements (which are displayed during
|
||||
// browsing/searching) into the DOM when, or shortly before, we need them,
|
||||
@@ -75,7 +73,7 @@ function initializeSiteTree() {
|
||||
let image;
|
||||
if (video.image) {
|
||||
image = document.createElement('img');
|
||||
image.src = rootPrefix + video.sitePath + video.image;
|
||||
image.src = window.hyper8.rootPrefix + video.sitePath + video.image;
|
||||
|
||||
if (video.lqip) {
|
||||
image.style.background = lqipToRadialGradient(video.lqip);
|
||||
@@ -105,11 +103,11 @@ function initializeSiteTree() {
|
||||
|
||||
video.element = document.createElement('a');
|
||||
video.element.classList.add('video');
|
||||
video.element.href = rootPrefix + video.sitePath + indexSuffix;
|
||||
video.element.href = window.hyper8.rootPrefix + video.sitePath + indexSuffix;
|
||||
video.element.appendChild(image);
|
||||
video.element.appendChild(divTitleDetails);
|
||||
|
||||
if (video.sitePath === sitePath) {
|
||||
if (video.sitePath === window.hyper8.sitePath) {
|
||||
video.element.classList.add('current');
|
||||
}
|
||||
|
||||
@@ -145,7 +143,7 @@ function initializeSiteTree() {
|
||||
|
||||
playlist.element = document.createElement('a');
|
||||
playlist.element.classList.add('playlist');
|
||||
playlist.element.href = rootPrefix + playlist.sitePath + indexSuffix;
|
||||
playlist.element.href = window.hyper8.rootPrefix + playlist.sitePath + indexSuffix;
|
||||
playlist.element.appendChild(playlist.expandToggle);
|
||||
playlist.element.appendChild(spanTitle);
|
||||
playlist.element.appendChild(spanInfo);
|
||||
@@ -160,7 +158,7 @@ function initializeSiteTree() {
|
||||
});
|
||||
}
|
||||
|
||||
if (playlist.sitePath === sitePath) {
|
||||
if (playlist.sitePath === window.hyper8.sitePath) {
|
||||
playlist.element.classList.add('current');
|
||||
}
|
||||
|
||||
@@ -197,7 +195,7 @@ function initializeSiteTree() {
|
||||
|
||||
collection.element = document.createElement('a');
|
||||
collection.element.classList.add('collection');
|
||||
collection.element.href = rootPrefix + collection.sitePath + indexSuffix;
|
||||
collection.element.href = window.hyper8.rootPrefix + collection.sitePath + indexSuffix;
|
||||
collection.element.appendChild(collection.expandToggle);
|
||||
|
||||
collection.element.appendChild(spanTitle);
|
||||
@@ -216,7 +214,7 @@ function initializeSiteTree() {
|
||||
});
|
||||
}
|
||||
|
||||
if (collection.sitePath === sitePath) {
|
||||
if (collection.sitePath === window.hyper8.sitePath) {
|
||||
collection.element.classList.add('current');
|
||||
}
|
||||
|
||||
|
||||
@@ -289,10 +289,6 @@ img {
|
||||
padding: .5rem;
|
||||
}
|
||||
.medium_font { font-size: 1.2rem; }
|
||||
.outline {
|
||||
flex-shrink: 0;
|
||||
min-width: 20rem;
|
||||
}
|
||||
.outro_playlist_context > a,
|
||||
.outro_playlist_context > button {
|
||||
align-items: center;
|
||||
@@ -634,9 +630,10 @@ img {
|
||||
.search input:focus::placeholder { opacity: 0; }
|
||||
.search:not(.query_present) button { display: none; }
|
||||
.sidebar {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-shrink: 0;
|
||||
min-width: 20rem;
|
||||
}
|
||||
.sidebar.hidden { display: none; }
|
||||
.site_title {
|
||||
color: var(--fg-2);
|
||||
font-size: 1.17rem;
|
||||
@@ -974,7 +971,7 @@ video {
|
||||
.feed_grid { gap: 2rem; }
|
||||
}
|
||||
@media (min-width: 80rem) {
|
||||
.outline {
|
||||
.sidebar {
|
||||
max-height: calc(100dvh - 4rem);
|
||||
overflow-y: auto;
|
||||
width: 24rem;
|
||||
|
||||
@@ -20,6 +20,7 @@ const speedControl = document.querySelector('#speed');
|
||||
const speedMultiplier = document.querySelector('#speed .multiplier');
|
||||
const timelineInput = document.querySelector('.timeline input');
|
||||
const togglePlaybackButton = document.querySelector('#toggle_playback');
|
||||
const togglePlaylistButton = document.querySelector('#toggle_playlist');
|
||||
const versionSelectorControl = document.querySelector('.selector_control.version');
|
||||
const versionSlices = document.querySelectorAll('.versions_ring g[data-src]');
|
||||
const versionsRing = document.querySelector('.versions_ring');
|
||||
@@ -436,6 +437,23 @@ speedControl.addEventListener('wheel', event => {
|
||||
|
||||
togglePlaybackButton.addEventListener('click', togglePlayback);
|
||||
|
||||
if (togglePlaylistButton) {
|
||||
const sidebar = document.querySelector('.sidebar');
|
||||
const storageKey = `hyper8HidePlaylist:${togglePlaylistButton.dataset.sitePath}`;
|
||||
|
||||
function togglePlaylist() {
|
||||
if (sidebar.classList.contains('hidden')) {
|
||||
sessionStorage.removeItem(storageKey);
|
||||
sidebar.classList.remove('hidden');
|
||||
} else {
|
||||
sessionStorage.setItem(storageKey, '')
|
||||
sidebar.classList.add('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
togglePlaylistButton.addEventListener('click', togglePlaylist);
|
||||
}
|
||||
|
||||
video.addEventListener('ended', () => {
|
||||
playerWrapper.dataset.state = 'outro';
|
||||
clearInterval(playbackUpdateInterval);
|
||||
|
||||
+4
-1
@@ -208,10 +208,13 @@ impl Layout {
|
||||
let t_video_player = translations.video_player;
|
||||
let body = formatdoc!(r##"
|
||||
<body>
|
||||
<script>
|
||||
window.hyper8 = {{ rootPrefix: '{root_prefix}', sitePath: '{site_path}' }};
|
||||
</script>
|
||||
<div class="layout">
|
||||
<header>
|
||||
<div class="navigation">
|
||||
<a class="home" data-root-prefix="{root_prefix}" data-site-path="{site_path}" href="{root_url}">
|
||||
<a class="home" href="{root_url}">
|
||||
{logo_polychrome_icon}
|
||||
<span class="title">{e_site_title}</span>
|
||||
</a>
|
||||
|
||||
+60
-31
@@ -50,20 +50,6 @@ pub fn video_html(
|
||||
let title = video.title_or_slug_or_generic_label(translations);
|
||||
let e_title = html_escape_outside_attribute(title);
|
||||
|
||||
let r_downloads = if video.download() && !video.versions.is_empty() {
|
||||
let t_download = translations.download;
|
||||
let t_download_permalink = translations.download_permalink;
|
||||
let download_icon = icons::DOWNLOAD;
|
||||
|
||||
formatdoc!(r#"
|
||||
<a href="{t_download_permalink}{index_suffix}">
|
||||
{download_icon} {t_download}
|
||||
</a>
|
||||
"#)
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
|
||||
let parent_context = if traversal.steps.len() > 1 {
|
||||
let parent_step = &traversal.steps[traversal.steps.len() - 2];
|
||||
parent_step.context_with_bubbles("../", site)
|
||||
@@ -80,7 +66,9 @@ pub fn video_html(
|
||||
video_prefix
|
||||
);
|
||||
|
||||
let copy_link_button = if video.copy_link() {
|
||||
let mut actions = Vec::new();
|
||||
|
||||
if video.copy_link() {
|
||||
let (content_key, content_value) = match &site.base_url {
|
||||
Some(base_url) => ("content", base_url.join_index(site, &video.site_path)),
|
||||
None => ("dynamic-url", String::new())
|
||||
@@ -88,25 +76,52 @@ pub fn video_html(
|
||||
|
||||
layout.add_clipboard_script();
|
||||
|
||||
copy_button(content_key, &content_value, translations.copy_link)
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
let copy_link = copy_button(content_key, &content_value, translations.copy_link);
|
||||
|
||||
let r_embed = if video.embedding() && site.base_url.is_some() && !video.versions.is_empty() {
|
||||
actions.push(copy_link);
|
||||
}
|
||||
|
||||
if video.embedding() && site.base_url.is_some() && !video.versions.is_empty() {
|
||||
let embed_icon = icons::EMBED;
|
||||
|
||||
let t_embed = &translations.embed;
|
||||
let t_embed_codes_permalink = &translations.embed_codes_permalink;
|
||||
|
||||
format!(r#"
|
||||
let embed_action = format!(r#"
|
||||
<a href="{t_embed_codes_permalink}{index_suffix}">
|
||||
{embed_icon} {t_embed}
|
||||
</a>
|
||||
"#)
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
"#);
|
||||
|
||||
actions.push(embed_action);
|
||||
}
|
||||
|
||||
if video.download() && !video.versions.is_empty() {
|
||||
let t_download = translations.download;
|
||||
let t_download_permalink = translations.download_permalink;
|
||||
let download_icon = icons::DOWNLOAD;
|
||||
|
||||
let download = formatdoc!(r#"
|
||||
<a href="{t_download_permalink}{index_suffix}">
|
||||
{download_icon} {t_download}
|
||||
</a>
|
||||
"#);
|
||||
|
||||
actions.push(download);
|
||||
}
|
||||
|
||||
if let Some(playlist) = playlist {
|
||||
let t_playlist = translations.playlist;
|
||||
let playlist_icon = icons::playlist(None);
|
||||
let playlist_site_path = playlist.site_path.normalized();
|
||||
let toggle_playlist = formatdoc!(r#"
|
||||
<button data-site-path="{playlist_site_path}" id="toggle_playlist">
|
||||
{playlist_icon} {t_playlist}
|
||||
</button>
|
||||
"#);
|
||||
|
||||
actions.push(toggle_playlist);
|
||||
}
|
||||
|
||||
let release_date = match video.release_date {
|
||||
Some(release_date) => {
|
||||
@@ -125,6 +140,17 @@ pub fn video_html(
|
||||
|
||||
let links_rendered = links(&site.language, &video.links);
|
||||
|
||||
let actions = if actions.is_empty() {
|
||||
String::new()
|
||||
} else {
|
||||
let actions_joined = actions.join("\n");
|
||||
formatdoc!(r#"
|
||||
<div class="actions align flex space">
|
||||
{actions_joined}
|
||||
</div>
|
||||
"#)
|
||||
};
|
||||
|
||||
let video_content = formatdoc!(r#"
|
||||
<div>
|
||||
<div class="video_info top">
|
||||
@@ -137,11 +163,7 @@ pub fn video_html(
|
||||
<div>
|
||||
{release_date}
|
||||
</div>
|
||||
<div class="actions align flex space">
|
||||
{copy_link_button}
|
||||
{r_embed}
|
||||
{r_downloads}
|
||||
</div>
|
||||
{actions}
|
||||
</div>
|
||||
{links_rendered}
|
||||
{description}
|
||||
@@ -159,12 +181,19 @@ pub fn video_html(
|
||||
Some(video)
|
||||
);
|
||||
|
||||
let playlist_site_path = playlist.site_path.normalized();
|
||||
formatdoc!(r#"
|
||||
<div class="layout_split">
|
||||
{video_content}
|
||||
<div class="outline">
|
||||
<div class="sidebar">
|
||||
{playlist_context}
|
||||
</div>
|
||||
<script>
|
||||
if (sessionStorage.getItem('hyper8HidePlaylist:{playlist_site_path}') !== null) {{
|
||||
const sidebar = document.currentScript.previousElementSibling;
|
||||
sidebar.classList.add('hidden');
|
||||
}}
|
||||
</script>
|
||||
</div>
|
||||
"#)
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ pub async fn index(context: Data<Arc<Context>>) -> HttpResponse {
|
||||
|
||||
fn empty_site_html(translations: &Translations) -> String {
|
||||
let collection_icon = icons::collection(translations.collection);
|
||||
let playlist_icon = icons::playlist(translations.playlist);
|
||||
let playlist_icon = icons::playlist(None);
|
||||
let video_icon = icons::video(translations.video);
|
||||
|
||||
let t_a_site_presenting_a_single_playlist_of_videos = translations.a_site_presenting_a_single_playlist_of_videos;
|
||||
|
||||
@@ -305,7 +305,7 @@ pub fn edit_page(
|
||||
translations
|
||||
);
|
||||
|
||||
let playlist_icon = icons::playlist(translations.playlist);
|
||||
let playlist_icon = icons::playlist(Some(translations.playlist));
|
||||
let playlist_title = playlist.title_or_slug_or_generic_label(translations);
|
||||
|
||||
let t_new_video = translations.new_video;
|
||||
|
||||
@@ -348,7 +348,7 @@ fn playlist_node(
|
||||
breadcrumbs.push(format!(r#"<a href="{exposed_edit_url}">{e_breadcrumb_title}</a>"#));
|
||||
}
|
||||
|
||||
let playlist_icon = icons::playlist(translations.playlist);
|
||||
let playlist_icon = icons::playlist(Some(translations.playlist));
|
||||
let playlist_visibility = Visibility::from_playlist(playlist);
|
||||
|
||||
let node = tree_node(
|
||||
|
||||
+9
-3
@@ -303,10 +303,16 @@ pub fn play(label: &str) -> String {
|
||||
"#)
|
||||
}
|
||||
|
||||
pub fn playlist(label: &str) -> String {
|
||||
/// Video viewport rectangles that are subtly stacked towards the distance, growing fainter with distance
|
||||
pub fn playlist(label: Option<&str>) -> String {
|
||||
let (aria_hidden, title) = match label {
|
||||
Some(label) => ("", format!("<title>{label}</title>")),
|
||||
None => (r#"aria-hidden="true""#, String::new())
|
||||
};
|
||||
|
||||
formatdoc!(r#"
|
||||
<svg width="1em" height="1em" version="1.1" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg">
|
||||
<title>{label}</title>
|
||||
<svg {aria_hidden} width="1em" height="1em" version="1.1" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg">
|
||||
{title}
|
||||
<path d="m15.502 29.753c-1.8558 2e-6 -3.3624 1.5043-3.3624 3.3601v18.998c0 0.92794 0.37558 1.7683 0.98368 2.3764 0.15202 0.15202 0.31779 0.29066 0.49657 0.41144 0.53661 0.36295 1.1856 0.5746 1.8822 0.5746h32.996c0.92794 0 1.7683-0.37794 2.3764-0.98604 0.15253-0.15238 0.28796-0.31729 0.40908-0.49657 0.36233-0.53632 0.57696-1.1839 0.57696-1.8798v-18.998c0-1.8558-1.5066-3.3601-3.3624-3.3601z"></path>
|
||||
<path d="m15.622 19.14c-1.8558 2e-6 -3.3613 1.5035-3.3613 3.3594v1.9473h39.479v-1.9473c0-1.8559-1.5055-3.3594-3.3613-3.3594h-20.527z" opacity=".666"></path>
|
||||
<path d="m15.622 8.5264c-1.8558 2e-6 -3.3613 1.5035-3.3613 3.3594v1.9473h39.479v-1.9473c0-1.8559-1.5055-3.3594-3.3613-3.3594h-20.527z" opacity=".333"></path>
|
||||
|
||||
Reference in New Issue
Block a user