Ethan Dalool
9a29048ccf
This makes the role of each css definition more clear, and could allow for cases where the side is sticky in wide mode but not sticky in narrow mode.
447 lines
13 KiB
HTML
447 lines
13 KiB
HTML
<!DOCTYPE html5>
|
|
<html>
|
|
|
|
{% macro shared_css() %}
|
|
<style>
|
|
h2, h3
|
|
{
|
|
margin-top: 0;
|
|
}
|
|
#album_metadata h2 .editor_input
|
|
{
|
|
font-size: inherit;
|
|
font-weight: inherit;
|
|
}
|
|
#description_text
|
|
{
|
|
font-family: initial;
|
|
padding: 8px;
|
|
}
|
|
|
|
#left
|
|
{
|
|
display: grid;
|
|
grid-row-gap: 30px;
|
|
grid-auto-rows: max-content;
|
|
}
|
|
#right
|
|
{
|
|
display: grid;
|
|
grid-row-gap: 8px;
|
|
grid-auto-rows: min-content;
|
|
padding: 8px;
|
|
background-color: var(--color_transparency);
|
|
}
|
|
|
|
#right > *
|
|
{
|
|
/* Fixes button_with_confirm, spinner, etc. from width-jumping on load,
|
|
because originally the button is occupying the full width and then becomes
|
|
small when it gets put into the holder */
|
|
margin-right: auto;
|
|
|
|
/* Fixes the button_with_confirm inputs from causing the overall row to
|
|
become wider than the containing parent when expanded. */
|
|
word-break: break-all;
|
|
}
|
|
#right input
|
|
{
|
|
margin: 0;
|
|
}
|
|
#right .confirm_holder_stage2
|
|
{
|
|
display: flex;
|
|
flex-direction: row;
|
|
}
|
|
#right .confirm_holder_stage2 span,
|
|
#right .confirm_holder_stage2 input
|
|
{
|
|
flex: 1;
|
|
min-width: 0;
|
|
}
|
|
|
|
.remove_child_button
|
|
{
|
|
display: none;
|
|
}
|
|
.remove_child_button:hover,
|
|
.album_card:hover .remove_child_button
|
|
{
|
|
display: initial;
|
|
}
|
|
</style>
|
|
{% endmacro %}
|
|
|
|
{% if album is not defined %} {## Album listing ###################################################}
|
|
|
|
<head>
|
|
{% import "header.html" as header %}
|
|
{% import "album_card.html" as album_card %}
|
|
<title>Albums</title>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
|
<link rel="stylesheet" href="/static/css/common.css">
|
|
<link rel="stylesheet" href="/static/css/etiquette.css">
|
|
<link rel="stylesheet" href="/static/css/photo_card.css">
|
|
{% if theme %}<link rel="stylesheet" href="/static/css/theme_{{theme}}.css">{% endif %}
|
|
<script src="/static/js/common.js"></script>
|
|
<script src="/static/js/api.js"></script>
|
|
|
|
{{shared_css()}}
|
|
</head>
|
|
|
|
|
|
<body>
|
|
{{header.make_header(session=session)}}
|
|
<div id="content_body" class="sticky_side_right sticky_bottom_right">
|
|
<div id="left">
|
|
<div id="album_list" class="panel">
|
|
<h2>Albums</h2>
|
|
{% for album in albums %}
|
|
{{album_card.create_album_card(album, view=view, draggable=true)}}
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
<div id="right">
|
|
{% if view != "list" %}
|
|
<a href="?view=list">List view</a>
|
|
{% else %}
|
|
<a href="?view=grid">Grid view</a>
|
|
{% endif %}
|
|
<button
|
|
class="green_button button_with_confirm"
|
|
data-is-input="1"
|
|
data-prompt="Album title"
|
|
data-cancel-class="gray_button"
|
|
data-onclick="return create_child(event.target.input_source.value);"
|
|
>
|
|
Create album
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
|
|
|
|
<script id="album_listing_script" type="text/javascript">
|
|
const ALBUM_ID = undefined;
|
|
</script>
|
|
|
|
{% else %} {## Individual album ###################################################################}
|
|
|
|
<head>
|
|
{% import "header.html" as header %}
|
|
{% import "album_card.html" as album_card %}
|
|
{% import "photo_card.html" as photo_card %}
|
|
{% import "clipboard_tray.html" as clipboard_tray %}
|
|
<title>{{album.display_name}} | Albums</title>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
|
<link rel="stylesheet" href="/static/css/common.css">
|
|
<link rel="stylesheet" href="/static/css/etiquette.css">
|
|
<link rel="stylesheet" href="/static/css/clipboard_tray.css">
|
|
<link rel="stylesheet" href="/static/css/photo_card.css">
|
|
{% if theme %}<link rel="stylesheet" href="/static/css/theme_{{theme}}.css">{% endif %}
|
|
<script src="/static/js/common.js"></script>
|
|
<script src="/static/js/api.js"></script>
|
|
<script src="/static/js/spinner.js"></script>
|
|
<script src="/static/js/editor.js"></script>
|
|
<script src="/static/js/hotkeys.js"></script>
|
|
<script src="/static/js/photo_clipboard.js"></script>
|
|
|
|
{{shared_css()}}
|
|
</head>
|
|
|
|
|
|
<body>
|
|
{{header.make_header(session=session)}}
|
|
<div id="content_body" class="sticky_side_right sticky_bottom_right">
|
|
<div id="right">
|
|
{% if view != "list" %}
|
|
<a href="?view=list">List view</a>
|
|
{% else %}
|
|
<a href="?view=grid">Grid view</a>
|
|
{% endif %}
|
|
|
|
<button
|
|
class="red_button button_with_confirm"
|
|
data-onclick="return delete_album_form();"
|
|
data-prompt="Delete Album?"
|
|
data-cancel-class="gray_button"
|
|
>
|
|
Delete
|
|
</button>
|
|
|
|
<button
|
|
class="green_button button_with_confirm"
|
|
data-is-input="1"
|
|
data-prompt="Album title"
|
|
data-cancel-class="gray_button"
|
|
data-onclick="return create_child(event.target.input_source.value);"
|
|
>
|
|
Create child
|
|
</button>
|
|
|
|
<button
|
|
class="green_button button_with_confirm"
|
|
data-is-input="1"
|
|
data-prompt="Child ID"
|
|
data-cancel-class="gray_button"
|
|
data-onclick="return add_child(event.target.input_source.value);"
|
|
>
|
|
Add child
|
|
</button>
|
|
|
|
{% set associated_directories = album.get_associated_directories() %}
|
|
<button
|
|
class="green_button button_with_spinner"
|
|
onclick="return refresh_associated_directories_form();"
|
|
data-spinner-delay="500"
|
|
{% if associated_directories %}
|
|
title="Pull from {{associated_directories|length}} directories"
|
|
{% else %}
|
|
title="No associated directories"
|
|
disabled
|
|
{% endif %}
|
|
>
|
|
Refresh directories
|
|
</button>
|
|
</div>
|
|
|
|
<div id="left">
|
|
<div id="hierarchy_self" class="panel">
|
|
<div id="album_metadata">
|
|
<h2><span
|
|
id="title_text"
|
|
data-editor-id="title"
|
|
data-editor-empty-text="{{album.id}}"
|
|
data-editor-placeholder="title"
|
|
>
|
|
{{-album.display_name-}}
|
|
</span></h2>
|
|
|
|
<pre
|
|
id="description_text"
|
|
data-editor-id="description"
|
|
data-editor-placeholder="description"
|
|
{% if not album.description %}class="hidden"{% endif %}
|
|
>
|
|
{{-album.description-}}
|
|
</pre>
|
|
</div>
|
|
<button class="green_button editor_toolbox_placeholder">Edit</button>
|
|
</div>
|
|
|
|
<div id="hierarchy_parents" class="panel">
|
|
{% set parents = album.get_parents() %}
|
|
{% if parents %}
|
|
<h3>{{parents|length}} Parents</h3>
|
|
{% for parent in parents %}
|
|
{{album_card.create_album_card(parent, view=view)}}
|
|
{% endfor %}
|
|
{% else %}
|
|
<h3>1 Parent</h3>
|
|
{{album_card.create_album_card("root", view=view)}}
|
|
{% endif %}
|
|
</div>
|
|
|
|
{% set sub_albums = album.get_children() %}
|
|
{% if sub_albums %}
|
|
<div id="hierarchy_children" class="panel">
|
|
<h3>{{sub_albums|length}} Children</h3>
|
|
{% for sub_album in sub_albums|sort(attribute='title') %}
|
|
{{album_card.create_album_card(sub_album, view=view, unlink_parent=album, draggable=true)}}
|
|
{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% set photos = album.get_photos() %}
|
|
{% if photos %}
|
|
<div id="hierarchy_photos" class="panel">
|
|
<h3>{{photos|length}} Photos</h3>
|
|
<div id="photo_list">
|
|
{% for photo in photos %}
|
|
{{photo_card.create_photo_card(photo, view=view)}}
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% set has_local_photos = photos|length > 0 %}
|
|
{% set has_child_photos = album.has_any_subalbum_photo() %}
|
|
{% if has_local_photos or has_child_photos %}
|
|
<div id="download_links" class="panel">
|
|
<h3>Download</h3>
|
|
{% if has_local_photos %}
|
|
<p><a id="download_link_single" href="/album/{{album.id}}.zip?recursive=no">These files – {{album.sum_bytes(recurse=False)|bytestring}}</a></p>
|
|
{% endif %}
|
|
{% if has_child_photos %}
|
|
<p><a id="download_link_recursive" href="/album/{{album.id}}.zip?recursive=yes">Include children – {{album.sum_bytes(recurse=True)|bytestring}}</a></p>
|
|
{% endif %}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
{{clipboard_tray.clipboard_tray()}}
|
|
<div class="my_clipboard_tray_toolbox">
|
|
<button class="green_button" onclick="return paste_photo_clipboard();">Add to this album</button>
|
|
<button class="red_button" onclick="return unpaste_photo_clipboard();">Remove from this album</button>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
|
|
|
|
<script id="album_individual_script" type="text/javascript">
|
|
const ALBUM_ID = "{{album.id}}";
|
|
|
|
function add_child(child_id)
|
|
{
|
|
if (! child_id.trim())
|
|
{
|
|
return;
|
|
}
|
|
api.albums.add_child(ALBUM_ID, child_id, common.refresh);
|
|
}
|
|
|
|
function delete_album_form()
|
|
{
|
|
api.albums.delete(ALBUM_ID, api.albums.callback_go_to_albums);
|
|
}
|
|
|
|
function refresh_associated_directories_form()
|
|
{
|
|
api.albums.refresh_directories(ALBUM_ID, common.refresh);
|
|
}
|
|
|
|
function paste_photo_clipboard()
|
|
{
|
|
const photo_ids = Array.from(photo_clipboard.clipboard);
|
|
api.albums.add_photos(ALBUM_ID, photo_ids, common.refresh);
|
|
}
|
|
function unpaste_photo_clipboard()
|
|
{
|
|
const photo_ids = Array.from(photo_clipboard.clipboard);
|
|
api.albums.remove_photos(ALBUM_ID, photo_ids, common.refresh);
|
|
}
|
|
|
|
function on_open(ed, edit_element_map, display_element_map)
|
|
{
|
|
ed.open();
|
|
edit_element_map["title"].focus();
|
|
}
|
|
|
|
function on_save(ed, edit_element_map, display_element_map)
|
|
{
|
|
function callback(response)
|
|
{
|
|
if (response.meta.status != 200)
|
|
{
|
|
ed.show_error("Status: " + response.meta.status);
|
|
return;
|
|
}
|
|
|
|
ed.save();
|
|
|
|
const title_display = display_element_map["title"];
|
|
const description_display = display_element_map["description"];
|
|
|
|
document.title = title_display.innerText + " | Albums";
|
|
|
|
if (description_display.innerText == "")
|
|
{
|
|
description_display.classList.add("hidden");
|
|
}
|
|
}
|
|
|
|
edit_element_map["title"].value = edit_element_map["title"].value.trim();
|
|
const title = edit_element_map["title"].value;
|
|
const description = edit_element_map["description"].value;
|
|
|
|
ed.show_spinner();
|
|
api.albums.edit(ALBUM_ID, title, description, callback);
|
|
}
|
|
|
|
function on_cancel(ed, edit_element_map, display_element_map)
|
|
{
|
|
ed.cancel();
|
|
if (display_element_map["description"].innerText == "")
|
|
{
|
|
display_element_map["description"].classList.add("hidden");
|
|
}
|
|
}
|
|
|
|
const title_text = document.getElementById("title_text");
|
|
const description_text = document.getElementById("description_text");
|
|
const ed = new editor.Editor([title_text, description_text], on_open, on_save, on_cancel);
|
|
</script>
|
|
|
|
{% endif %} {## Shared ############################################################################}
|
|
|
|
<script id="album_shared_script" type="text/javascript">
|
|
|
|
function create_child(title)
|
|
{
|
|
if (! title)
|
|
{
|
|
title = undefined;
|
|
}
|
|
const parent_id = ALBUM_ID;
|
|
api.albums.create(title, parent_id, api.albums.callback_follow);
|
|
}
|
|
|
|
function get_album_card_from_child(element)
|
|
{
|
|
/*
|
|
Given an element which is known to be a child of an album card, navigate up
|
|
the parent tree until we find that album card.
|
|
This is used to make drag-and-drop work even when you start your drag by
|
|
clicking on the album's thumbnail, title, etc.
|
|
*/
|
|
while (! element.classList.contains("album_card"))
|
|
{
|
|
element = element.parentElement;
|
|
}
|
|
return element;
|
|
}
|
|
|
|
function on_album_drag_start(event)
|
|
{
|
|
const album_card = get_album_card_from_child(event.target);
|
|
event.dataTransfer.setData("text/plain", album_card.id);
|
|
}
|
|
function on_album_drag_end(event)
|
|
{
|
|
}
|
|
function on_album_drag_over(event)
|
|
{
|
|
event.preventDefault();
|
|
}
|
|
function on_album_drag_drop(event)
|
|
{
|
|
const child = document.getElementById(event.dataTransfer.getData("text"));
|
|
const child_id = child.dataset.id;
|
|
const parent = event.currentTarget;
|
|
const parent_id = parent.dataset.id;
|
|
event.dataTransfer.clearData();
|
|
|
|
if (child_id == parent_id)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const child_title = child.querySelector('.album_card_title').textContent.trim();
|
|
const parent_title = parent.querySelector('.album_card_title').textContent.trim();
|
|
if (confirm(`Move\n${child_title}\ninto\n${parent_title}?`))
|
|
{
|
|
if (ALBUM_ID)
|
|
{
|
|
api.albums.add_child(parent_id, child_id, null);
|
|
api.albums.remove_child(ALBUM_ID, child_id, common.refresh);
|
|
}
|
|
else
|
|
{
|
|
api.albums.add_child(parent_id, child_id, common.refresh);
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
</html>
|