Add the photo_clipboard feature with localStorage.

photo_card objects now have a checkbox which adds them
to the clipboard. No pasting or other operations yet.
This commit is contained in:
voussoir 2017-12-09 17:48:56 -08:00
parent f69d9d409d
commit 55f7da7bb2
5 changed files with 138 additions and 11 deletions

View file

@ -130,26 +130,33 @@ is hovered over.
display:inline; display:inline;
} }
.photo_card
{
background-color: #ffffd4;
}
.photo_card_list .photo_card_list
{ {
display: grid; display: grid;
grid-template-columns: 1fr auto; grid-template-columns: auto 1fr auto;
grid-template-rows: auto; grid-template-rows: auto;
grid-template-areas: grid-template-areas:
"filename metadata"; "checkbox filename metadata";
max-width: 800px; max-width: 800px;
margin: 8px; margin: 8px;
padding: 4px; padding: 4px;
background-color: #ffffd4;
} }
.photo_card_list:hover .photo_card_list:hover
{ {
box-shadow: 2px 2px 5px 0px rgba(0,0,0,0.25); box-shadow: 2px 2px 5px 0px rgba(0,0,0,0.25);
} }
.photo_card_list .photo_card_selector_checkbox
{
grid-area: checkbox;
}
.photo_card_grid .photo_card_grid
{ {
position: relative;
display: inline-grid; display: inline-grid;
grid-template-columns: auto auto; grid-template-columns: auto auto;
grid-template-rows: auto 1fr auto; grid-template-rows: auto 1fr auto;
@ -165,8 +172,12 @@ is hovered over.
border-radius: 8px; border-radius: 8px;
box-shadow: 2px 2px 5px 0px rgba(0,0,0,0.25); box-shadow: 2px 2px 5px 0px rgba(0,0,0,0.25);
}
background-color: #ffffd4; .photo_card_grid .photo_card_selector_checkbox
{
position:absolute;
left:5px;
top:5px;
} }
.photo_card_thumbnail .photo_card_thumbnail
{ {
@ -187,7 +198,6 @@ is hovered over.
The min-width:100% + width:0 prevent the info div from controlling The min-width:100% + width:0 prevent the info div from controlling
card size, so we can prioritize the thumbnail instead. card size, so we can prioritize the thumbnail instead.
*/ */
align-self: start;
justify-self: start; justify-self: start;
grid-area: filename; grid-area: filename;
@ -202,6 +212,14 @@ is hovered over.
font-size: 12.8px; font-size: 12.8px;
} }
.photo_card_grid .photo_card_filename
{
align-self: start;
}
.photo_card_list .photo_card_filename
{
align-self: center;
}
.photo_card_filename:hover .photo_card_filename:hover
{ {
overflow: visible; overflow: visible;

View file

@ -0,0 +1,103 @@
var photo_clipboard = new Set();
function load_photo_clipboard(event)
{
console.log("Loading photo clipboard");
var stored = localStorage.getItem("photo_clipboard");
if (stored === null)
{
if (photo_clipboard.size != 0)
{
photo_clipboard = new Set();
}
}
else
{
photo_clipboard = new Set(JSON.parse(stored));
}
var photo_divs = Array.from(document.getElementsByClassName("photo_card"));
photo_divs.forEach(apply_check);
return photo_clipboard;
}
function save_photo_clipboard()
{
console.log("Saving photo clipboard");
var serialized = JSON.stringify(Array.from(photo_clipboard));
localStorage.setItem("photo_clipboard", serialized);
}
function apply_check(photo_div)
{
var checkbox = photo_div.getElementsByClassName("photo_card_selector_checkbox")[0];
if (photo_clipboard.has(photo_div.dataset.id))
{
checkbox.checked = true;
}
else
{
checkbox.checked = false;
}
}
var previous_photo_select;
function on_photo_select(event)
{
if (event.target.checked)
{
action = function(photo_div)
{
photo_div.getElementsByClassName("photo_card_selector_checkbox")[0].checked = true;
photo_clipboard.add(photo_div.dataset.id);
}
}
else
{
action = function(photo_div)
{
photo_div.getElementsByClassName("photo_card_selector_checkbox")[0].checked = false;
photo_clipboard.delete(photo_div.dataset.id);
}
}
if (event.shiftKey && previous_photo_select !== undefined)
{
var current_photo_div = event.target.parentElement;
var previous_photo_div = previous_photo_select.target.parentElement;
var photo_divs = Array.from(current_photo_div.parentElement.children);
var current_index = photo_divs.indexOf(current_photo_div);
var previous_index = photo_divs.indexOf(previous_photo_div);
var slice;
if (current_index == previous_index)
{
slice = [current_photo_div];
}
else if (previous_index < current_index)
{
slice = photo_divs.slice(previous_index, current_index + 1);
}
else
{
slice = photo_divs.slice(current_index, previous_index + 1);
}
slice.forEach(action);
}
else
{
var photo_div = event.target.parentElement;
action(photo_div);
}
previous_photo_select = event;
save_photo_clipboard();
}
function onpageload()
{
window.addEventListener("storage", load_photo_clipboard, false);
load_photo_clipboard();
}
document.addEventListener("DOMContentLoaded", onpageload);

View file

@ -8,6 +8,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"/> <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<link rel="stylesheet" href="/static/common.css"> <link rel="stylesheet" href="/static/common.css">
<script src="/static/common.js"></script> <script src="/static/common.js"></script>
<script src="/static/photoclipboard.js"></script>
<style> <style>
p p

View file

@ -17,12 +17,13 @@
{% macro create_photo_card(photo, view="grid") %} {% macro create_photo_card(photo, view="grid") %}
{% if view == "list" %} {% if view == "list" %}
<div class="photo_card_list"> <div class="photo_card photo_card_list" data-id="{{photo.id}}">
<input type="checkbox" class="photo_card_selector_checkbox" onclick="on_photo_select(event)"/>
<span class="photo_card_filename"><a target="_blank" href="/photo/{{photo.id}}">{{photo.basename}}</a></span> <span class="photo_card_filename"><a target="_blank" href="/photo/{{photo.id}}">{{photo.basename}}</a></span>
<a class="photo_card_metadata" target="_blank" href="/file/{{photo.id}}.{{photo.extension}}">{{photo.bytestring()}}</a> <a class="photo_card_metadata" target="_blank" href="/file/{{photo.id}}.{{photo.extension}}">{{photo.bytestring()}}</a>
</div> </div>
{% else %} {% else %}
{% if photo.thumbnail %} {% if photo.thumbnail %}
{% set thumbnail_src = "/thumbnail/" + photo.id + ".jpg" %} {% set thumbnail_src = "/thumbnail/" + photo.id + ".jpg" %}
{% else %} {% else %}
@ -39,6 +40,7 @@
{% for tag in photo.tags() %} {% for tag in photo.tags() %}
{% do tag_names_title.append(tag.name) %} {% do tag_names_title.append(tag.name) %}
{% endfor %} {% endfor %}
{% set tag_names_title = ", ".join(tag_names_title) %} {% set tag_names_title = ", ".join(tag_names_title) %}
{% if tag_names_title %} {% if tag_names_title %}
{% set tag_names_inner = "T" %} {% set tag_names_inner = "T" %}
@ -54,7 +56,7 @@
{% set metadata_inner = "{m}{d}, ".format(m=metadata_inner, d=photo.duration_string) %} {% set metadata_inner = "{m}{d}, ".format(m=metadata_inner, d=photo.duration_string) %}
{% endif %} {% endif %}
<div class="photo_card_grid"> <div class="photo_card photo_card_grid" data-id="{{photo.id}}">
<a class="photo_card_thumbnail" target="_blank" href="/photo/{{photo.id}}"> <a class="photo_card_thumbnail" target="_blank" href="/photo/{{photo.id}}">
<img height="150" src="{{thumbnail_src}}"> <img height="150" src="{{thumbnail_src}}">
</a> </a>
@ -69,6 +71,8 @@
{{- metadata_inner|safe -}} {{- metadata_inner|safe -}}
<a target="_blank" href="/file/{{photo.id}}.{{photo.extension}}">{{photo.bytestring()}}</a> <a target="_blank" href="/file/{{photo.id}}.{{photo.extension}}">{{photo.bytestring()}}</a>
</span> </span>
<input type="checkbox" class="photo_card_selector_checkbox" onclick="on_photo_select(event)"/>
</div> </div>
{% endif %} {% endif %}
{% endmacro %} {% endmacro %}

View file

@ -9,6 +9,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"/> <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<link rel="stylesheet" href="/static/common.css"> <link rel="stylesheet" href="/static/common.css">
<script src="/static/common.js"></script> <script src="/static/common.js"></script>
<script src="/static/photoclipboard.js"></script>
<style> <style>
form form