From 01ccc5672365342bb06e7602728cad5898b14921 Mon Sep 17 00:00:00 2001 From: Marcus Date: Tue, 10 Sep 2024 01:45:45 -0700 Subject: [PATCH] implement datalist replacement --- src/features/quick_reblog.css | 48 ++++++++++++++++++++++++++++++++++- src/features/quick_reblog.js | 36 ++++++++++++++++---------- 2 files changed, 69 insertions(+), 15 deletions(-) diff --git a/src/features/quick_reblog.css b/src/features/quick_reblog.css index 0d1bb0fb7..7148ca55d 100644 --- a/src/features/quick_reblog.css +++ b/src/features/quick_reblog.css @@ -4,7 +4,6 @@ box-shadow: 0 0 15px 0 rgba(0,0,0,.5); padding: 2px; border-radius: 3px; - overflow: hidden; color: rgb(var(--black)); font-size: .875rem; font-weight: normal; @@ -140,6 +139,53 @@ div:first-child + span + #quick-reblog, line-height: 1; } +#quick-reblog .tags-input-wrapper > input { + width: 100%; + box-sizing: border-box; +} + +#quick-reblog .tags-input-wrapper:not(:focus, :focus-within) > .tag-suggestions { + display: none; +} + +#quick-reblog .tag-suggestions { + position: absolute; + z-index: 1; + max-width: 250px; + margin: 7px; + padding: 2px; + + border-radius: 4px; + color: rgb(var(--black), 0.65); + background-color: rgb(var(--white)); + box-shadow: 0 0 15px 0 rgba(0, 0, 0, .5); +} + +#quick-reblog .tag-suggestions::before { + position: absolute; + bottom: 100%; + left: 10px; + + content: ''; + border-left: 9px solid transparent; + border-right: 9px solid transparent; + border-bottom: 9px solid rgb(var(--white)); +} + +#quick-reblog .tag-suggestions > div { + max-height: 250px; + overflow-y: scroll; +} + +#quick-reblog .tag-suggestions button { + padding: 0.75ch 1ch; + padding-right: calc(1ch + 1em + 1ch); + max-width: 100%; + + overflow-x: hidden; + white-space: nowrap; +} + #quick-reblog .action-buttons { display: flex; flex-direction: row; diff --git a/src/features/quick_reblog.js b/src/features/quick_reblog.js index a43a16541..7c7f4f5cf 100644 --- a/src/features/quick_reblog.js +++ b/src/features/quick_reblog.js @@ -31,12 +31,12 @@ const tagsInput = dom( 'input', { placeholder: 'Tags (comma separated)', - autocomplete: 'off', - list: 'quick-reblog-tag-suggestions' + autocomplete: 'off' }, { keydown: event => event.stopPropagation() } ); -const tagSuggestions = dom('datalist', { id: 'quick-reblog-tag-suggestions' }); +const tagSuggestions = dom('div', { class: 'tag-suggestions' }); +const tagsInputWrapper = dom('div', { class: 'tags-input-wrapper' }, null, [tagsInput]); const actionButtons = dom('fieldset', { class: 'action-buttons' }); const reblogButton = dom('button', null, null, ['Reblog']); reblogButton.dataset.state = 'published'; @@ -44,7 +44,7 @@ const queueButton = dom('button', null, null, ['Queue']); queueButton.dataset.state = 'queue'; const draftButton = dom('button', null, null, ['Draft']); draftButton.dataset.state = 'draft'; -[blogSelectorContainer, commentInput, quickTagsList, tagsInput, tagSuggestions, actionButtons].forEach(element => popupElement.appendChild(element)); +[blogSelectorContainer, commentInput, quickTagsList, tagsInputWrapper, actionButtons].forEach(element => popupElement.appendChild(element)); let lastPostID; let timeoutID; @@ -80,28 +80,35 @@ const onBlogSelectorChange = () => { }; blogSelector.addEventListener('change', onBlogSelectorChange); -const renderTagSuggestions = () => { - tagSuggestions.textContent = ''; - if (!showTagSuggestions) return; +tagSuggestions.addEventListener('click', event => { + if (event.target.dataset.value) { + tagsInput.value += `${event.target.dataset.value}, `; + tagsInput.focus(); + renderTagSuggestions(); + } +}); +const renderTagSuggestions = () => { const currentTags = tagsInput.value .split(',') .map(tag => tag.trim().toLowerCase()) .filter(tag => tag !== ''); - const includeSpace = !tagsInput.value.endsWith(' ') && tagsInput.value.trim() !== ''; - const tagsToSuggest = suggestableTags .filter(tag => !currentTags.includes(tag.toLowerCase())) - .filter((tag, index, array) => array.indexOf(tag) === index) - .map(tag => `${tagsInput.value}${includeSpace ? ' ' : ''}${tag}`); + .filter((tag, index, array) => array.indexOf(tag) === index); - tagSuggestions.append(...tagsToSuggest.map(value => dom('option', { value }))); + tagSuggestions.replaceChildren( + dom('div', null, null, tagsToSuggest.map(value => dom('button', { 'data-value': value }, null, [value]))) + ); }; const updateTagSuggestions = () => { - if (tagsInput.value.trim().endsWith(',') || tagsInput.value.trim() === '') { + if (showTagSuggestions && (tagsInput.value.trim().endsWith(',') || tagsInput.value.trim() === '')) { renderTagSuggestions(); + tagsInputWrapper.append(tagSuggestions); + } else { + tagSuggestions.remove(); } }; @@ -150,7 +157,7 @@ const showPopupOnHover = ({ currentTarget }) => { if (postAuthor) suggestableTags.push(postAuthor); if (rebloggedRootName) suggestableTags.push(rebloggedRootName); suggestableTags.push(postType({ trail, content, layout })); - renderTagSuggestions(); + updateTagSuggestions(); }); } lastPostID = thisPostID; @@ -386,6 +393,7 @@ export const clean = async function () { popupElement.remove(); blogSelector.removeEventListener('change', updateRememberedBlog); + tagSuggestions.remove(); browser.storage.onChanged.removeListener(updateQuickTags); onNewPosts.removeListener(processPosts);