Autocomplete existing tags in the upload and manage forms
A small shared widget (static/tagcomplete.js) completes the tag after the last comma from the existing tag list, keeping earlier tags intact. It is wired to every comma-separated tag input: the upload form, and the manage page's per-row edit field and bulk add/remove fields. Suggestions come from /tags.json, which the generator already writes on every build and nginx serves statically, so no server change is needed; the list is fetched once, cached, and invalidated after an edit that may add a tag. Keyboard navigation (up/down, enter/tab, escape) and click selection are supported.
This commit is contained in:
1 parent
bf75c9fe8e
commit
c4aa345914
4 files changed
+183
-3
No files matched your search
@@ -105,9 +105,9 @@
|
||||
|
||||
<div class="bulkbar" id="bulkbar">
|
||||
<span class="sel" id="sel">0 selected</span>
|
||||
<input type="text" id="bulk-add" placeholder="add tags" autocomplete="off">
|
||||
<input type="text" id="bulk-add" placeholder="add tags" autocomplete="off" data-tagcomplete>
|
||||
<button class="btn" id="btn-add">Add</button>
|
||||
<input type="text" id="bulk-remove" placeholder="remove tags" autocomplete="off">
|
||||
<input type="text" id="bulk-remove" placeholder="remove tags" autocomplete="off" data-tagcomplete>
|
||||
<button class="btn" id="btn-remove">Remove</button>
|
||||
<button class="btn danger" id="btn-del-sel">Delete selected</button>
|
||||
</div>
|
||||
@@ -118,6 +118,7 @@
|
||||
|
||||
<div id="toast"></div>
|
||||
|
||||
<script src="/static/tagcomplete.js"></script>
|
||||
<script>
|
||||
const list = document.getElementById('list');
|
||||
const countEl = document.getElementById('count');
|
||||
@@ -166,6 +167,7 @@
|
||||
// Set values via property to avoid HTML-escaping issues in attributes.
|
||||
el.querySelector('.f-caption').value = p.caption;
|
||||
el.querySelector('.f-tags').value = (p.tags || []).join(', ');
|
||||
if (window.TagComplete) window.TagComplete.attach(el.querySelector('.f-tags'));
|
||||
|
||||
el.querySelector('.pick').addEventListener('change', refreshBulk);
|
||||
el.querySelector('.f-save').addEventListener('click', () => save(el, p.slug));
|
||||
@@ -203,6 +205,7 @@
|
||||
form.append('tags', el.querySelector('.f-tags').value);
|
||||
try {
|
||||
await post('/api/edit', form);
|
||||
if (window.TagComplete) window.TagComplete.invalidate();
|
||||
flash('Saved ' + slug, true);
|
||||
} catch (err) { flash('Save failed: ' + err.message, false); }
|
||||
}
|
||||
@@ -254,6 +257,7 @@
|
||||
const res = await post('/api/tags', form);
|
||||
const j = await res.json().catch(() => ({}));
|
||||
if (!j.updated) { flash('No posts updated', false); return; }
|
||||
if (window.TagComplete) window.TagComplete.invalidate();
|
||||
document.getElementById(field).value = '';
|
||||
flash('Updated tags on ' + j.updated + ' post(s)', true);
|
||||
await load();
|
||||
|
||||
@@ -163,13 +163,14 @@
|
||||
|
||||
<div class="field">
|
||||
<label for="tags">Tags</label>
|
||||
<input type="text" id="tags" placeholder="comma-separated, lowercase" autocomplete="off">
|
||||
<input type="text" id="tags" placeholder="comma-separated, lowercase" autocomplete="off" data-tagcomplete>
|
||||
</div>
|
||||
|
||||
<button class="submit-btn" id="submit-btn" disabled>Upload photo</button>
|
||||
<div id="feedback"></div>
|
||||
</div>
|
||||
|
||||
<script src="/static/tagcomplete.js"></script>
|
||||
<script>
|
||||
const fileInput = document.getElementById('file-input');
|
||||
const preview = document.getElementById('preview');
|
||||
|
||||
Reference in new issue
Block a user