summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Pages/Shared/_Layout.cshtml2
-rw-r--r--Pages/Shared/_Layout.cshtml.css6
-rw-r--r--Pages/TagDefinitions.cshtml12
-rw-r--r--Pages/TagDefinitions.cshtml.css4
-rw-r--r--Pages/ViewMedia.cshtml66
-rw-r--r--Pages/ViewMedia.cshtml.cs3
-rw-r--r--Pages/ViewMedia.cshtml.css38
-rw-r--r--wwwroot/styles/global.css67
8 files changed, 173 insertions, 25 deletions
diff --git a/Pages/Shared/_Layout.cshtml b/Pages/Shared/_Layout.cshtml
index 9cdb952..4c80500 100644
--- a/Pages/Shared/_Layout.cshtml
+++ b/Pages/Shared/_Layout.cshtml
@@ -18,7 +18,7 @@
<a href="/">Home</a>
<a href="/TagDefinitions">Tags</a>
</div>
- <div id="content" style="overflow:@(ViewBag.ContentScroll ? "auto" : "hidden");margin:@(ViewBag.ContentMargin ?? "0");">
+ <div id="content" style="overflow-y:@(ViewBag.ContentScroll ? "auto" : "hidden");padding:@(ViewBag.ContentMargin ?? "0");">
@RenderBody()
</div>
</body>
diff --git a/Pages/Shared/_Layout.cshtml.css b/Pages/Shared/_Layout.cshtml.css
index 5b2ba65..22e36db 100644
--- a/Pages/Shared/_Layout.cshtml.css
+++ b/Pages/Shared/_Layout.cshtml.css
@@ -4,15 +4,21 @@
}
div#navbar > a {
+ color: white;
display: inline-block;
padding: 20px 20px 20px 20px;
}
div#navbar > a:hover {
background: rgba(255, 255, 255, 0.4);
+ filter: none;
}
div#navbar > a:active {
background: #fff;
color: var(--col-navbar-bg);
+}
+
+#content {
+ flex: 1 1 calc(100vh - 119px);
} \ No newline at end of file
diff --git a/Pages/TagDefinitions.cshtml b/Pages/TagDefinitions.cshtml
index a05f5d5..7bf1790 100644
--- a/Pages/TagDefinitions.cshtml
+++ b/Pages/TagDefinitions.cshtml
@@ -27,7 +27,9 @@
async function deleteTagDefinition() {
var dialog = document.getElementById('delete-dialog');
- var resp = await fetch(`/api/tag/def/${dialog.dataset.guid}`, {
+ var tagDefId = dialog.dataset.guid;
+
+ var resp = await fetch(`/api/tag/def/${tagDefId}`, {
method: 'delete'
});
@@ -35,7 +37,9 @@
alert('Error deleting tag definition!');
showDeleteDialog(false);
} else {
- window.location.reload()
+ var rows = Array.from(document.getElementsByTagName('tr'));
+ rows.find(r => r.dataset.guid == tagDefId).remove();
+ showDeleteDialog(false);
}
}
@@ -65,12 +69,12 @@
<th></th>
</tr>
@foreach(var tagDef in Model.TagDefinitions) {
- <tr>
+ <tr data-guid="@tagDef.Guid">
<td>@tagDef.Guid</td>
<td>@tagDef.Source</td>
<td>@tagDef.Namespace</td>
<td>@tagDef.Name</td>
- <td><button onclick="showDeleteDialog('@tagDef.Guid')">Delete</button></td>
+ <td><a href="javascript:showDeleteDialog('@tagDef.Guid');">Delete</a></td>
</tr>
}
</table>
diff --git a/Pages/TagDefinitions.cshtml.css b/Pages/TagDefinitions.cshtml.css
index 0a9e226..93001c7 100644
--- a/Pages/TagDefinitions.cshtml.css
+++ b/Pages/TagDefinitions.cshtml.css
@@ -9,8 +9,4 @@ div.button-container {
table#tag-definitions td:first-child {
font-family: 'Lucida Console';
-}
-
-table#tag-definitions td > button {
- margin-top: 0;
} \ No newline at end of file
diff --git a/Pages/ViewMedia.cshtml b/Pages/ViewMedia.cshtml
index 3d0ce4a..e37bbf1 100644
--- a/Pages/ViewMedia.cshtml
+++ b/Pages/ViewMedia.cshtml
@@ -7,8 +7,9 @@
<link rel="stylesheet" type="text/css" href="@(nameof(HyperBooru)).styles.css"/>
<script>
+ var mediaId = new URL(window.location.href).searchParams.get('m');
+
async function deleteMedia() {
- var mediaId = new URL(window.location.href).searchParams.get('m');
var resp = await fetch('/media/' + mediaId, { method: 'delete' });
if(resp.ok) {
window.location.href = '/';
@@ -17,10 +18,44 @@
}
}
+ async function applyTags() {
+ var checkboxes = Array.from(document
+ .getElementById('tag-definitions')
+ .getElementsByTagName('input'));
+
+ var tagDefIds = checkboxes
+ .filter(cb => cb.checked)
+ .map(cb => cb.id.replace(/^tagdef-/, ''));
+
+ var pendingRequests = tagDefIds
+ .map(id => fetch(`/api/tag/${mediaId}/${id}`, { method: 'POST' }));
+
+ var responses = await Promise.all(pendingRequests);
+
+ if(responses.some(r => !r.ok && r.status != 400)) {
+ alert('Error setting tags!');
+ }
+ showTagDialog(false);
+ }
+
+ async function removeTag(e, tagDefId) {
+ var resp = await fetch(`/api/tag/${mediaId}/${tagDefId}`, { method: 'DELETE' });
+ if(!resp.ok && resp.status != 400) {
+ alert('Error removing tag!');
+ } else {
+ e.closest('tr').remove();
+ }
+ }
+
function showDeleteDialog(visible) {
document.getElementById('delete-dialog').classList.toggle('visible', visible);
}
+ function showTagDialog(visible) {
+ document.getElementById('tag-dialog').classList.toggle('visible', visible);
+ document.querySelector('div#tag-dialog input').focus();
+ }
+
function selectPane(tab) {
var tabs = Array.from(document.querySelectorAll('div#metadata-header > a'));
@@ -86,11 +121,22 @@
<div id="metadata-tags">
<table class="data-table">
<tr>
+ <th>Source</th>
+ <th>Namespace</th>
<th>Tag Name</th>
+ <th></th>
</tr>
+ @foreach(var tag in Model.Media.Tags.Select(t => t.TagDefinition)) {
+ <tr>
+ <td>@tag.Source</td>
+ <td>@tag.Namespace</td>
+ <td>@tag.Name</td>
+ <td><a href="javascript:;" onclick="removeTag(this, '@tag.Guid')">Delete</a></td>
+ </tr>
+ }
</table>
<div class="button-container">
- <button>Add Tag</button>
+ <button onclick="showTagDialog(true)">Add Tag</button>
</div>
</div>
</div>
@@ -104,3 +150,19 @@
<button onclick="deleteMedia()">Confirm</button>
</div>
</div>
+
+<div id="tag-dialog" class="dialog">
+ <p>Select one or more tag(s) to add</p>
+ <hr/>
+ <input type="text" placeholder="Search"/>
+ <div id="tag-definitions">
+ @foreach(var tagdef in Model.TagDefinitions) {
+ <input type="checkbox" id="tagdef-@tagdef.Guid"/>
+ <label for="tagdef-@tagdef.Guid">@tagdef.Name</label>
+ }
+ </div>
+ <div class="button-container">
+ <button onclick="showTagDialog(false)" class="secondary">Cancel</button>
+ <button onclick="applyTags()">Accept</button>
+ </div>
+</div>
diff --git a/Pages/ViewMedia.cshtml.cs b/Pages/ViewMedia.cshtml.cs
index fe8d150..476ea40 100644
--- a/Pages/ViewMedia.cshtml.cs
+++ b/Pages/ViewMedia.cshtml.cs
@@ -6,6 +6,9 @@ namespace HyperBooru.Pages;
public class ViewMediaModel : PageModel {
public DbMedia Media { get; private set; }
+ public IEnumerable<DbTagDefinition> TagDefinitions =>
+ db.TagDefinitions;
+
private HyperBooruDbContext db;
public ViewMediaModel(HyperBooruDbContext db) =>
diff --git a/Pages/ViewMedia.cshtml.css b/Pages/ViewMedia.cshtml.css
index 4041fa8..622de48 100644
--- a/Pages/ViewMedia.cshtml.css
+++ b/Pages/ViewMedia.cshtml.css
@@ -40,6 +40,7 @@ div#metadata-header > a.selected {
div#metadata-header > a:hover {
background: rgba(255, 255, 255, 0.4);
+ filter: none;
}
div#metadata-fileinfo > table th {
@@ -51,7 +52,42 @@ div#metadata-fileinfo > table td {
font-size: 8pt;
}
+div#metadata-tags > table td {
+ font-size: 8pt;
+}
+
div.button-container {
display: flex;
justify-content: flex-end;
-} \ No newline at end of file
+}
+
+div#tag-dialog {
+ max-height: 400px;
+}
+
+div#tag-dialog div#tag-definitions {
+ overflow-y: auto;
+ user-select: none;
+}
+
+div#tag-dialog div#tag-definitions label {
+ background: #555;
+ border-radius: 10px;
+ display: inline-block;
+ font-size: 10pt;
+ margin: 0 5px 5px 0;
+ padding: 5px 7px 5px 7px;
+ transition: background 0.1s linear;
+}
+
+div#tag-dialog div#tag-definitions label:hover {
+ background: #777;
+}
+
+div#tag-dialog div#tag-definitions input:checked + label {
+ background: #aaa;
+}
+
+div#tag-dialog div#tag-definitions input {
+ display: none;
+}
diff --git a/wwwroot/styles/global.css b/wwwroot/styles/global.css
index 2b007f8..969c531 100644
--- a/wwwroot/styles/global.css
+++ b/wwwroot/styles/global.css
@@ -1,24 +1,36 @@
:root {
- --col-accent-pri: #0aa;
- --col-bg: #222;
- --col-dialog-bg: #333;
- --col-navbar-bg: var(--col-accent-pri);
- --col-button-pri: var(--col-accent-pri);
- --col-button-sec: #555;
+ --col-accent-pri: #0aa;
+ --col-accent-pri-hl: #0cc;
+ --col-bg: #222;
+ --col-dialog-bg: #333;
+ --col-navbar-bg: var(--col-accent-pri);
+ --col-button-pri: var(--col-accent-pri);
+ --col-button-pri-hl: var(--col-accent-pri-hl);
+ --col-button-sec: #555;
+ --col-button-sec-hl: #777;
+ --col-scrollbar: #666666;
+ --col-scrollbar-hover: #aaaaaa;
}
body {
- margin: 0;
- background: var(--col-bg);
- color: white;
- font-family: 'Trebuchet MS', 'Lucida Sans Unicode';
+ background: var(--col-bg);
+ color: white;
+ display: flex;
+ flex-direction: column;
+ font-family: 'Trebuchet MS', 'Lucida Sans Unicode';
+ margin: 0;
+ overflow: hidden;
}
a {
- color: white;
+ color: var(--col-accent-pri);
text-decoration: none;
}
+a:hover {
+ filter: brightness(1.5);
+}
+
button, input[type=submit] {
color: white;
background: var(--col-button-pri);
@@ -28,18 +40,29 @@ button, input[type=submit] {
height: 30px;
margin: 10px 5px 0 5px;
padding: 0 9px 0 9px;
+ user-select: none;
}
button.secondary {
background: var(--col-button-sec);
}
+button.secondary:hover {
+ background: var(--col-button-sec-hl);
+}
+
+button.secondary:active {
+ background: white;
+ color: var(--col-button-sec);
+}
+
button:hover, input[type=submit]:hover {
- filter: grayscale(0.9) brightness(2.5);
+ background: var(--col-button-pri-hl);
}
button:active, input[type=submit]:active {
- filter: grayscale(0.9) brightness(3.5);
+ background: white;
+ color: var(--col-button-pri);
}
input {
@@ -100,3 +123,21 @@ div.dialog.visible {
opacity: 1;
visibility: visible;
}
+
+::-webkit-scrollbar {
+ width: 10px;
+ height: 10px;
+}
+
+::-webkit-scrollbar-thumb {
+ background: var(--col-scrollbar);
+ border-radius: 10px;
+}
+
+::-webkit-scrollbar-thumb:hover {
+ background: var(--col-scrollbar-hover);
+}
+
+::-webkit-scrollbar-corner {
+ opacity: 0;
+}