aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPinapelz <yukais@pinapelz.com>2025-02-06 02:46:23 -0800
committerPinapelz <yukais@pinapelz.com>2025-02-06 02:46:23 -0800
commit35d6d27a7e9bc741e6b3648b6573369411fd7861 (patch)
treea47768e78c5df0d84ab887f87ee6e68a3959eca0
parent716f1fab54ef6c6b3a70f9cd4b2b70f7e9cdd7a8 (diff)
add image pop-up modal
-rw-r--r--static/index.js77
-rw-r--r--static/styles.css68
-rw-r--r--templates/index.html16
3 files changed, 139 insertions, 22 deletions
diff --git a/static/index.js b/static/index.js
index bc850e8..ce54149 100644
--- a/static/index.js
+++ b/static/index.js
@@ -17,7 +17,6 @@
const MAX_NAME_LEN = 200;
const DEFAULT_TIERS = ['S','A','B','C','D','E','F'];
const TIER_COLORS = [
- // from S to F
'#ff6666',
'#f0a731',
'#f4d95b',
@@ -55,14 +54,11 @@ function reset_row(row) {
});
}
-// Removes all rows from the tierlist, alongside their content.
-// Also empties the untiered images.
function hard_reset_list() {
tierlist_div.innerHTML = '';
untiered_images.innerHTML = '';
}
-// Places back all the tierlist content into the untiered pool.
function soft_reset_list() {
tierlist_div.querySelectorAll('.row').forEach(reset_row);
unsaved_changes = true;
@@ -84,7 +80,6 @@ window.addEventListener('load', () => {
bind_title_events();
document.getElementById('load-img-input').addEventListener('input', (evt) => {
- // @Speed: maybe we can do some async stuff to optimize this
let images = document.querySelector('.images');
for (let file of evt.target.files) {
let reader = new FileReader();
@@ -97,7 +92,6 @@ window.addEventListener('load', () => {
}
});
- // Allow copy-pasting image from clipboard
document.onpaste = (evt) => {
let clip_data = evt.clipboardData || evt.originalEvent.clipboardData;
let items = clip_data.items;
@@ -159,6 +153,43 @@ window.addEventListener('load', () => {
});
void try_load_tierlist_json();
+
+ const modal = document.getElementById('image-modal');
+ const modalImg = document.getElementById('modal-img');
+ const modalDesc = document.getElementById('modal-description');
+ const modalSave = document.getElementById('modal-save');
+ const modalClose = document.querySelector('.modal .close');
+ let currentModalImage = null;
+
+ function showModal(img) {
+ currentModalImage = img;
+ modal.style.display = 'flex';
+ modalImg.src = img.src;
+ modalDesc.value = img.dataset.description || '';
+ }
+
+ modalClose.addEventListener('click', () => {
+ modal.style.display = 'none';
+ });
+
+ modalSave.addEventListener('click', () => {
+ if (currentModalImage) {
+ currentModalImage.dataset.description = modalDesc.value;
+ }
+ modal.style.display = 'none';
+ });
+
+ window.addEventListener('click', (evt) => {
+ if (evt.target == modal) {
+ modal.style.display = 'none';
+ }
+ });
+
+ tierlist_div.addEventListener('click', (evt) => {
+ if (evt.target.tagName.toUpperCase() === 'IMG') {
+ showModal(evt.target);
+ }
+ });
});
function create_img_with_src(src) {
@@ -167,7 +198,7 @@ function create_img_with_src(src) {
img.style.userSelect = 'none';
img.classList.add('draggable');
img.draggable = true;
- img.ondragstart = "event.dataTransfer.setData('text/plain', null)";
+ img.ondragstart = function(evt) { evt.dataTransfer.setData('text/plain', null); };
img.addEventListener('mousedown', (evt) => {
dragged_image = evt.target;
dragged_image.classList.add("dragged");
@@ -198,7 +229,10 @@ function save_tierlist(filename) {
});
serialized_tierlist.rows[i].imgs = [];
row.querySelectorAll('img').forEach((img) => {
- serialized_tierlist.rows[i].imgs.push(img.src);
+ serialized_tierlist.rows[i].imgs.push({
+ src: img.src,
+ description: img.dataset.description || ''
+ });
});
});
@@ -206,7 +240,10 @@ function save_tierlist(filename) {
if (untiered_imgs.length > 0) {
serialized_tierlist.untiered = [];
untiered_imgs.forEach((img) => {
- serialized_tierlist.untiered.push(img.src);
+ serialized_tierlist.untiered.push({
+ src: img.src,
+ description: img.dataset.description || ''
+ });
});
}
@@ -219,8 +256,9 @@ function load_tierlist(serialized_tierlist) {
let ser_row = serialized_tierlist.rows[idx];
let elem = add_row(idx, ser_row.name);
- for (let img_src of ser_row.imgs ?? []) {
- let img = create_img_with_src(img_src);
+ for (let img_obj of ser_row.imgs ?? []) {
+ let img = create_img_with_src(img_obj.src);
+ img.dataset.description = img_obj.description || '';
let td = document.createElement('span');
td.classList.add('item');
td.appendChild(img);
@@ -234,8 +272,9 @@ function load_tierlist(serialized_tierlist) {
if (serialized_tierlist.untiered) {
let images = document.querySelector('.images');
- for (let img_src of serialized_tierlist.untiered) {
- let img = create_img_with_src(img_src);
+ for (let img_obj of serialized_tierlist.untiered) {
+ let img = create_img_with_src(img_obj.src);
+ img.dataset.description = img_obj.description || '';
images.appendChild(img);
}
}
@@ -276,7 +315,6 @@ function make_accept_drop(elem) {
let dragged_image_parent = dragged_image.parentNode;
if (dragged_image_parent.tagName.toUpperCase() === 'SPAN' &&
dragged_image_parent.classList.contains('item')) {
- // We were already in a tier
let containing_tr = dragged_image_parent.parentNode;
containing_tr.removeChild(dragged_image_parent);
} else {
@@ -287,11 +325,14 @@ function make_accept_drop(elem) {
td.appendChild(dragged_image);
let items_container = elem.querySelector('.items');
if (!items_container) {
- // Quite lazy hack for <section class='images'>
items_container = elem;
}
items_container.appendChild(td);
-
+ if (elem !== untiered_images) {
+ dragged_image.draggable = false;
+ } else {
+ dragged_image.draggable = true;
+ }
unsaved_changes = true;
});
}
@@ -457,7 +498,6 @@ function bind_trash_events() {
if (dragged_image_parent.tagName.toUpperCase() === 'SPAN' &&
dragged_image_parent.classList.contains('item'))
{
- // We were already in a tier
let containing_tr = dragged_image_parent.parentNode;
containing_tr.removeChild(dragged_image_parent);
}
@@ -492,7 +532,6 @@ function is_url (str) {
}
}
-// Fetches a tierlist JSON file from the 'url' query parameter and loads it
async function try_load_tierlist_json () {
const load_from_url = new URLSearchParams(window.location.search).get('url');
if (load_from_url !== null && is_url(load_from_url)) {
@@ -503,4 +542,4 @@ async function try_load_tierlist_json () {
load_tierlist(result);
} catch (e) { console.error(e); }
}
-} \ No newline at end of file
+}
diff --git a/static/styles.css b/static/styles.css
index a0cfef2..6f2d5f3 100644
--- a/static/styles.css
+++ b/static/styles.css
@@ -242,4 +242,70 @@ img.draggable.dragged, .button:active {
font-size: small;
color: #ccc;
padding: 0px 10px;
-} \ No newline at end of file
+}
+
+.modal {
+ position: fixed;
+ z-index: 1000;
+ left: 0;
+ top: 0;
+ width: 100%;
+ height: 100%;
+ background-color: rgba(0, 0, 0, 0.6);
+ display: none;
+ align-items: center;
+ justify-content: center;
+}
+
+.modal-content {
+ background-color: white;
+ padding: 30px;
+ border-radius: 10px;
+ box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.5);
+ width: 90%;
+ max-width: 800px;
+ text-align: center;
+}
+
+.close {
+ color: #aaa;
+ float: right;
+ font-size: 28px;
+ font-weight: bold;
+ cursor: pointer;
+}
+
+.close:hover {
+ color: black;
+}
+
+#modal-img {
+ max-width: 100%;
+ max-height: 40vh;
+ border-radius: 5px;
+ margin-bottom: 10px;
+}
+
+#modal-description {
+ width: 100%;
+ height: 25vh;
+ padding: 10px;
+ margin-top: 10px;
+ border: 1px solid #ccc;
+ border-radius: 5px;
+ font-size: 14px;
+}
+
+#modal-save {
+ background: #4CAF50;
+ color: white;
+ border: none;
+ padding: 10px 20px;
+ cursor: pointer;
+ margin-top: 10px;
+ border-radius: 5px;
+}
+
+#modal-save:hover {
+ background: #45a049;
+}
diff --git a/templates/index.html b/templates/index.html
index 15ee038..4b98bdc 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -4,7 +4,7 @@
<meta charset='utf-8' lang='en'/>
<title>Offline TierMaker</title>
<link rel="stylesheet" href="/static/styles.css">
- <script src="/static/index.js"></script>
+ <script src="/static/index.js" defer></script>
</head>
<body>
<div class='title'>
@@ -20,24 +20,28 @@
<div class='buttons-container'>
<div class='button'>
<label for='load-img-input'>
+ Add
<img src='plus.png' title='Add images'/>
</label>
<input id='load-img-input' type='file' accept='image/*' multiple/>
</div>
<div class='button'>
<label for='reset-list-input'>
+ R
<img src='reset.png' title='Reset list'>
</label>
<input id='reset-list-input' type='button' />
</div>
<div class='button'>
<label for='export-input'>
+ E
<img src='export.png' title='Export'>
</label>
<input id='export-input' type='button' />
</div>
<div class='button'>
<label for='import-input'>
+ I
<img src='import.png' title='Import'/>
</label>
<input id='import-input' type='file' accept='.json' multiple/>
@@ -49,7 +53,7 @@
</div>
</section>
<div class="top-container">
- <img id='trash' src='trash_bin.png' title='Delete image (drag it over here)'></img>
+ <img id='trash' src='trash_bin.png' title='Delete image (drag it over here)'>
<div class='button'>
<label for='toggle-layout'>
<img src='toggle_layout.png' title='Toggle Layout'/>
@@ -57,5 +61,13 @@
<input id='toggle-layout' type='button'/>
</div>
</div>
+ <div id="image-modal" class="modal">
+ <div class="modal-content">
+ <span class="close">&times;</span>
+ <img id="modal-img" src="" alt="Image preview" />
+ <textarea id="modal-description" placeholder="Enter description here"></textarea>
+ <button id="modal-save">Save</button>
+ </div>
+ </div>
</body>
</html> \ No newline at end of file
send patches to the email below
yukais@pinapelz.com
include the subject [PATCH repo_name]
pinapelz.com
homepage