Allow editing of scraped values (#80)

* Allow editing scraped title + description (#80)

* Fix edit button hijacking form submit
This commit is contained in:
Sascha Ißbrücker 2021-04-04 10:16:40 +02:00 committed by GitHub
parent c083997399
commit 2d19e97212
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 92 additions and 22 deletions

View file

@ -97,12 +97,41 @@ ul.bookmark-list {
.bookmarks-form { .bookmarks-form {
.btn.form-icon {
padding: 0;
width: 20px;
height: 20px;
visibility: hidden;
color: $gray-color;
&:focus,
&:hover,
&:active,
&.active {
color: $gray-color-dark;
}
> svg {
width: 20px;
height: 20px;
}
}
.has-icon-right > input, .has-icon-right > textarea {
padding-right: 30px;
}
.has-icon-right > input:placeholder-shown ~ .btn.form-icon,
.has-icon-right > textarea:placeholder-shown ~ .btn.form-icon {
visibility: visible;
}
.form-icon.loading { .form-icon.loading {
visibility: hidden; visibility: hidden;
} }
.form-input-hint.bookmark-exists { .form-input-hint.bookmark-exists {
visibility: hidden; display: none;
color: $warning-color; color: $warning-color;
a { a {
@ -163,6 +192,7 @@ $bulk-edit-transition-duration: 400ms;
span.confirmation { span.confirmation {
display: flex; display: flex;
} }
span.confirmation button { span.confirmation button {
padding: 0; padding: 0;
} }

View file

@ -14,12 +14,13 @@
</div> </div>
{% endif %} {% endif %}
<div class="form-input-hint bookmark-exists"> <div class="form-input-hint bookmark-exists">
This URL is already bookmarked. You can <a href="#">edit</a> it or you can overwrite the existing bookmark by saving this form. This URL is already bookmarked. You can <a href="#">edit</a> it or you can overwrite the existing bookmark
by saving this form.
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="{{ form.tag_string.id_for_label }}" class="form-label">Tags</label> <label for="{{ form.tag_string.id_for_label }}" class="form-label">Tags</label>
{{ form.tag_string|add_class:"form-input" }} {{ form.tag_string|add_class:"form-input"|attr:"autocomplete:off" }}
<div class="form-input-hint"> <div class="form-input-hint">
Enter any number of tags separated by space and <strong>without</strong> the hash (#). If a tag does not Enter any number of tags separated by space and <strong>without</strong> the hash (#). If a tag does not
exist it will be exist it will be
@ -30,8 +31,16 @@
<div class="form-group has-icon-right"> <div class="form-group has-icon-right">
<label for="{{ form.title.id_for_label }}" class="form-label">Title</label> <label for="{{ form.title.id_for_label }}" class="form-label">Title</label>
<div class="has-icon-right"> <div class="has-icon-right">
{{ form.title|add_class:"form-input" }} {{ form.title|add_class:"form-input"|attr:"autocomplete:off" }}
<i class="form-icon loading"></i> <i class="form-icon loading"></i>
<a class="btn btn-link form-icon" title="Edit title from website">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
<path d="M17.414 2.586a2 2 0 00-2.828 0L7 10.172V13h2.828l7.586-7.586a2 2 0 000-2.828z"/>
<path fill-rule="evenodd"
d="M2 6a2 2 0 012-2h4a1 1 0 010 2H4v10h10v-4a1 1 0 112 0v4a2 2 0 01-2 2H4a2 2 0 01-2-2V6z"
clip-rule="evenodd"/>
</svg>
</a>
</div> </div>
<div class="form-input-hint"> <div class="form-input-hint">
Optional, leave empty to use title from website. Optional, leave empty to use title from website.
@ -43,6 +52,14 @@
<div class="has-icon-right"> <div class="has-icon-right">
{{ form.description|add_class:"form-input"|attr:"rows:4" }} {{ form.description|add_class:"form-input"|attr:"rows:4" }}
<i class="form-icon loading"></i> <i class="form-icon loading"></i>
<a class="btn btn-link form-icon" title="Edit description from website">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
<path d="M17.414 2.586a2 2 0 00-2.828 0L7 10.172V13h2.828l7.586-7.586a2 2 0 000-2.828z"/>
<path fill-rule="evenodd"
d="M2 6a2 2 0 012-2h4a1 1 0 010 2H4v10h10v-4a1 1 0 112 0v4a2 2 0 01-2 2H4a2 2 0 01-2-2V6z"
clip-rule="evenodd"/>
</svg>
</a>
</div> </div>
<div class="form-input-hint"> <div class="form-input-hint">
Optional, leave empty to use description from website. Optional, leave empty to use description from website.
@ -82,49 +99,72 @@
/** /**
* - Pre-fill title and description placeholders with metadata from website as soon as URL changes * - Pre-fill title and description placeholders with metadata from website as soon as URL changes
* - Show hint if URL is already bookmarked * - Show hint if URL is already bookmarked
* - Setup buttons that allow editing of scraped website values
*/ */
(function init() { (function init() {
const urlInput = document.getElementById('{{ form.url.id_for_label }}'); const urlInput = document.getElementById('{{ form.url.id_for_label }}');
const titleInput = document.getElementById('{{ form.title.id_for_label }}'); const titleInput = document.getElementById('{{ form.title.id_for_label }}');
const descriptionInput = document.getElementById('{{ form.description.id_for_label }}'); const descriptionInput = document.getElementById('{{ form.description.id_for_label }}');
const editedBookmarkId = {{ bookmark_id }} const editedBookmarkId = {{ bookmark_id }};
urlInput.addEventListener('input', checkUrl); function toggleLoadingIcon(input, show) {
const icon = input.parentNode.querySelector('i.form-icon');
icon.style['visibility'] = show ? 'visible' : 'hidden';
}
function updatePlaceholder(input, value) {
if (value) {
input.setAttribute('placeholder', value);
} else {
input.removeAttribute('placeholder');
}
}
function checkUrl() { function checkUrl() {
toggleIcon(titleInput, true); toggleLoadingIcon(titleInput, true);
toggleIcon(descriptionInput, true); toggleLoadingIcon(descriptionInput, true);
updatePlaceholder(titleInput, null);
updatePlaceholder(descriptionInput, null);
const websiteUrl = encodeURIComponent(urlInput.value); const websiteUrl = encodeURIComponent(urlInput.value);
const requestUrl = `{% url 'bookmarks:api-root' %}bookmarks/check?url=${websiteUrl}`; const requestUrl = `{% url 'bookmarks:api-root' %}bookmarks/check?url=${websiteUrl}`;
fetch(requestUrl) fetch(requestUrl)
.then(response => response.json()) .then(response => response.json())
.then(data => { .then(data => {
const metadata = data.metadata const metadata = data.metadata;
titleInput.setAttribute('placeholder', metadata.title || ''); updatePlaceholder(titleInput, metadata.title);
descriptionInput.setAttribute('placeholder', metadata.description || ''); updatePlaceholder(descriptionInput, metadata.description);
toggleIcon(titleInput, false); toggleLoadingIcon(titleInput, false);
toggleIcon(descriptionInput, false); toggleLoadingIcon(descriptionInput, false);
// Display hint if URL is already bookmarked // Display hint if URL is already bookmarked
const bookmarkExistsHint = document.querySelector('.form-input-hint.bookmark-exists') const bookmarkExistsHint = document.querySelector('.form-input-hint.bookmark-exists');
const editExistingBookmarkLink = bookmarkExistsHint.querySelector('a') const editExistingBookmarkLink = bookmarkExistsHint.querySelector('a');
if(data.bookmark && data.bookmark.id !== editedBookmarkId) { if (data.bookmark && data.bookmark.id !== editedBookmarkId) {
bookmarkExistsHint.style['visibility'] = 'visible' bookmarkExistsHint.style['display'] = 'block';
editExistingBookmarkLink.href = data.bookmark.edit_url editExistingBookmarkLink.href = data.bookmark.edit_url;
} else { } else {
bookmarkExistsHint.style['visibility'] = 'hidden' bookmarkExistsHint.style['display'] = 'none';
} }
}); });
} }
function toggleIcon(input, show) { function setupEditAutoValueButton(input) {
const icon = input.parentNode.querySelector('i.form-icon'); const editAutoValueButton = input.parentNode.querySelector('.btn.form-icon');
icon.style['visibility'] = show ? 'visible' : 'hidden'; if (!editAutoValueButton) return;
editAutoValueButton.addEventListener('click', function (event) {
event.preventDefault();
input.value = input.getAttribute('placeholder');
input.focus();
input.select();
});
} }
if (urlInput.value) checkUrl(); if (urlInput.value) checkUrl();
urlInput.addEventListener('input', checkUrl);
setupEditAutoValueButton(titleInput);
setupEditAutoValueButton(descriptionInput);
})(); })();
</script> </script>
</div> </div>