Sync label changes

This commit is contained in:
Jackson Harper
2023-09-28 16:08:47 +08:00
parent c9b178ca5c
commit 901bdd2564
2 changed files with 335 additions and 79 deletions

View File

@ -279,7 +279,6 @@
}
function toggleRow(rowId) {
console.log('currentToastEl: ', currentToastEl)
const container = currentToastEl.shadowRoot.querySelector(rowId)
const initialState = container?.getAttribute('data-state')
const rows = currentToastEl.shadowRoot.querySelectorAll(
@ -361,7 +360,7 @@
})
}
function createLabelRow(label, idx) {
function createLabelRow(label) {
const element = document.createElement('button')
const dot = document.createElement('span')
dot.style = 'width:10px;height:10px;border-radius:1000px;'
@ -384,9 +383,8 @@
element.appendChild(check)
element.onclick = labelClick
element.onkeydown = labelKeyDown
element.onkeydown = labelEditorKeyDownHandler
element.setAttribute('data-label-id', label.id)
element.setAttribute('data-label-idx', idx)
element.setAttribute(
'data-label-selected',
label['selected'] ? 'on' : 'off'
@ -421,68 +419,117 @@
if (label) {
label.selected = toggledValue
}
const labelList = event.target.form.querySelector('#label-list')
const labelInput = event.target.form.querySelector(
'#omnivore-edit-label-input'
)
if (toggledValue) {
addLabel(labelList, labelInput, label.name)
} else {
removeLabel(labelList, label.id)
}
}
function labelKeyDown(event) {
function backspaceOnLastItem(labelsList, labelsInput) {
// Get the last <li> item before the <li><input item
const lastItem =
labelsInput.closest('#label-entry-item').previousElementSibling
if (lastItem) {
const backspaced = lastItem.getAttribute('data-label-backspaced')
if (backspaced) {
removeLabel(
labelsInput.closest('#label-list'),
lastItem.getAttribute('data-label-id')
)
} else {
lastItem.setAttribute('data-label-backspaced', 'on')
}
}
}
function labelEditorClickHandler(event) {
const input = event.target.querySelector('#omnivore-edit-label-input')
if (input && event.target != input) {
input.focus()
return
}
}
function clearBackspacedLabels(form) {
const selected = form.querySelectorAll('.label[data-label-backspaced="on"]')
selected.forEach((node) => {
node.removeAttribute('data-label-backspaced')
})
}
function labelEditorKeyDownHandler(event) {
event.cancelBubble = true
if (event.stopPropogation) {
event.stopPropogation()
}
// If any labels have been backspaced into (so they have the selected outline), clear their state
if (event.target.form && event.key.toLowerCase() !== 'backspace') {
clearBackspacedLabels(event.target.form)
}
switch (event.key.toLowerCase()) {
case 'arrowup': {
if (
event.target ==
event.target.form.querySelector('#omnivore-edit-label-text')
) {
if (event.target.id == 'omnivore-edit-label-input') {
return
}
const idx = event.target.getAttribute('data-label-idx')
let prevIdx = idx && Number(idx) != NaN ? Number(idx) - 1 : 0
if (
event.target ==
event.target.form.querySelector('#omnivore-save-button')
) {
// Focus the last label index
const maxItemIdx = Math.max(
...Array.from(
event.target.form.querySelectorAll(`button[data-label-idx]`)
).map((b) => Number(b.getAttribute('data-label-idx')))
)
if (maxItemIdx != NaN) {
prevIdx = maxItemIdx
}
if (!event.target.getAttribute('data-label-id')) {
return
}
const prev = event.target.form.querySelector(
`button[data-label-idx='${prevIdx}']`
)
if (prev) {
let prev = event.target.previousElementSibling
if (prev && prev.getAttribute('data-label-id')) {
prev.focus()
} else {
// Focus the text area
event.target.form.querySelector('#omnivore-edit-label-text')?.focus()
event.target.form.querySelector('#omnivore-edit-label-input')?.focus()
}
event.preventDefault()
break
}
case 'arrowdown': {
const idx = event.target.getAttribute('data-label-idx')
const nextIdx = idx && Number(idx) != NaN ? Number(idx) + 1 : 0
const next = event.target.form.querySelector(
`button[data-label-idx='${nextIdx}']`
)
if (next) {
next.focus()
let next = undefined
if (event.target.id == 'omnivore-edit-label-input') {
idx = event.target.getAttribute('data-label-id')
next = event.target
.closest('#omnivore-edit-labels-form')
.querySelector('#omnivore-edit-labels-list')
.querySelector('[data-label-id]')
} else {
// Focus the save button
event.target.form.querySelector('.omnivore-save-button')?.focus()
next = event.target.nextElementSibling
}
if (next && next.getAttribute('data-label-id')) {
next.focus()
}
event.preventDefault()
break
}
case 'backspace': {
if (
event.target.id == 'omnivore-edit-label-input' &&
event.target.value.length == 0
) {
const labelList = event.target.form.querySelector('#label-list')
backspaceOnLastItem(labelList, event.target)
}
break
}
case 'enter': {
if (event.target.id == 'omnivore-edit-label-input') {
if (event.target.value) {
const labelList = event.target.form.querySelector('#label-list')
addLabel(labelList, event.target, event.target.value)
}
event.preventDefault()
return
}
const labelId = event.target.getAttribute('data-label-id')
toggleLabel(event, labelId)
event.preventDefault()
@ -529,7 +576,6 @@
currentToastEl.shadowRoot.querySelector(
'#omnivore-add-note-form'
).onsubmit = (event) => {
console.log('submitting form: ', event)
updateStatusBox('#omnivore-add-note-status', 'loading', 'Adding note...')
browserApi.runtime.sendMessage({
@ -585,6 +631,143 @@
}
}
function getRandomColor() {
const colors = [
'#FF5D99',
'#7CFF7B',
'#FFD234',
'#7BE4FF',
'#CE88EF',
'#EF8C43',
]
const randomIndex = Math.floor(Math.random() * colors.length)
return colors[randomIndex]
}
function getTempUUID() {
return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, (c) =>
(
c ^
(crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))
).toString(16)
)
}
function addLabel(labelList, labelInput, labelValue) {
// first check if the label is already entered:
const existingLabel = labels.find((l) => l.name === labelValue)
const labelEntryItem = labelList.querySelector('#label-entry-item')
const inputItem = labelEntryItem.querySelector('#omnivore-edit-label-input')
// Handle case where label is already selected
if (
existingLabel &&
labelList.querySelector(`[data-label-id='${existingLabel.id}']`)
) {
const labelItem = labelList.querySelector(
`[data-label-id='${existingLabel.id}']`
)
labelItem.setAttribute('data-item-highlighted', 'on')
setTimeout(() => {
labelItem.style.borderColor = 'rgb(222, 222, 222)'
}, 500)
if (inputItem) {
inputItem.value = ''
inputItem.focus()
updateLabels(undefined)
}
return
}
const labelColor = existingLabel ? existingLabel.color : getRandomColor()
const labelElem = document.createElement('li')
labelElem.classList.add('label')
labelElem.innerHTML = `
<span style="width: 10px; height: 10px; border-radius: 1000px; background-color: ${labelColor};"></span>
${labelValue}
<button class="label-remove-button">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="#6A6968" viewBox="0 0 256 256">
<rect width="256" height="256" fill="none"></rect><line x1="200" y1="56" x2="56" y2="200" stroke="#6A6968" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"></line>
<line x1="200" y1="200" x2="56" y2="56" stroke="#6A6968" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"></line>
</svg>
</button>
`
labelList.insertBefore(labelElem, labelEntryItem)
labelInput.value = ''
const form = labelList.closest('#omnivore-edit-labels-form')
if (existingLabel) {
const element = form.querySelector(
`[data-label-id='${existingLabel.id}']`
)
existingLabel.selected = true
element.setAttribute('data-label-selected', 'on')
labelElem.setAttribute('data-label-id', existingLabel.id)
} else {
// insert a toggle row at the top
const rowList = form.querySelector('#omnivore-edit-labels-list')
const newLabel = {
id: getTempUUID(),
color: labelColor,
name: labelValue,
temporary: true,
selected: true,
}
labels.push(newLabel)
labelElem.setAttribute('data-label-id', newLabel.id)
// Now prepend a label in the rows at the bottom
const rowHtml = createLabelRow(newLabel)
const firstRow = rowList.querySelector('button[data-label-id]')
rowHtml.setAttribute('data-label-selected', 'on')
rowList.insertBefore(rowHtml, firstRow)
}
if (inputItem) {
inputItem.focus()
updateLabels(undefined)
}
syncLabelChanges()
}
function removeLabel(labelList, labelID) {
const form = labelList.closest('#omnivore-edit-labels-form')
const element = labelList.querySelector(`[data-label-id='${labelID}']`)
if (element) {
element.remove()
}
const rowElement = form.querySelector(`[data-label-id='${labelID}']`)
if (rowElement) {
rowElement.setAttribute('data-label-selected', 'off')
}
updateLabels()
}
function syncLabelChanges() {
console.log('syncLabels')
updateStatusBox(
'#omnivore-edit-labels-status',
'loading',
'Updating Labels...',
undefined
)
const labelIds = labels.filter((l) => l['selected']).map((l) => l.id)
// browserApi.runtime.sendMessage({
// - action: ACTIONS.SetLabels,
// - payload: {
// - ctx: ctx,
// - labelIds: labelIds,
// - },
// - })
}
async function editLabels() {
cancelAutoDismiss()
@ -594,47 +777,33 @@
toggleRow('#omnivore-edit-labels-row')
currentToastEl.shadowRoot
.querySelector('#omnivore-edit-label-text')
.querySelector('#omnivore-edit-label-input')
?.focus()
const list = currentToastEl.shadowRoot.querySelector(
'#omnivore-edit-labels-list'
)
currentToastEl.shadowRoot
.querySelector('#omnivore-edit-label-text')
.addEventListener('input', function () {
updateLabels(this.value)
})
currentToastEl.shadowRoot.querySelector(
'#omnivore-edit-label-text'
).onkeydown = labelKeyDown
'#omnivore-edit-label-input'
).onkeydown = labelEditorKeyDownHandler
currentToastEl.shadowRoot.querySelector(
'#omnivore-edit-label-editor'
).onclick = labelEditorClickHandler
currentToastEl.shadowRoot
.querySelector('#omnivore-edit-label-input')
.addEventListener('input', (event) => {
updateLabels(event.target.value)
})
if (list) {
list.innerHTML = ''
labels.forEach(function (label, idx) {
const rowHtml = createLabelRow(label, idx)
const rowHtml = createLabelRow(label)
list.appendChild(rowHtml)
})
}
currentToastEl.shadowRoot.querySelector(
'#omnivore-edit-labels-form'
).onsubmit = (event) => {
event.preventDefault()
const statusBox = currentToastEl.shadowRoot.querySelector(
'#omnivore-edit-labels-status'
)
statusBox.innerText = 'Updating labels...'
const labelIds = labels.filter((l) => l['selected']).map((l) => l.id)
browserApi.runtime.sendMessage({
action: ACTIONS.SetLabels,
payload: {
ctx: ctx,
labelIds: labelIds,
},
})
}
}
async function updateLabels(filterValue) {
@ -648,13 +817,13 @@
.filter(
(l) => l.name.toLowerCase().indexOf(filterValue.toLowerCase()) > -1
)
.forEach(function (label, idx) {
const rowHtml = createLabelRow(label, idx)
.forEach(function (label) {
const rowHtml = createLabelRow(label)
list.appendChild(rowHtml)
})
} else {
labels.forEach(function (label, idx) {
const rowHtml = createLabelRow(label, idx)
labels.forEach(function (label) {
const rowHtml = createLabelRow(label)
list.appendChild(rowHtml)
})
}

View File

@ -68,6 +68,11 @@
line-height: 20px;
text-align: center;
}
#omnivore-toast-container #omnivore-edit-labels-status {
height: 22px;
padding-top: 10px;
}
#omnivore-toast-button-row {
gap: 5px;
align-items: center;
@ -228,7 +233,7 @@
max-height: 200px;
gap: 5px;
color: #3B3A38;
margin-top: 15px;
margin-top: 0px;
margin-bottom: 10px;
}
@ -331,6 +336,86 @@
}
}
</style>
<style>
.label-editor {
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
display: inline-block;
background-color: var(--colors-thBackground2);
border: 1px solid transparent;
border-radius: 6px;
padding: 5px;
line-height: 2;
cursor: text;
font-size: 12px;
width: calc(100% - 10px);
min-height: 28px;
}
.label-list {
flex-wrap: wrap;
justify-content: start;
display: flex;
width: 100%;
align-items: center;
padding: 0px;
box-sizing: border-box;
height: auto;
flex: 0 0 auto;
overflow-y: auto;
margin: 0;
list-style: none;
gap: 5px;
}
#omnivore-toast-container .omnivore-toast-func-row .label-remove-button {
display: flex;
align-items: center;
justify-content: center;
color: transparent;
border: none;
background-color: transparent;
cursor: pointer;
padding: 0px;
}
#omnivore-edit-labels-row .label-input {
box-sizing: content-box;
font-size: 16px;
min-width: 2px;
border: none;
outline: none;
padding: 0px;
height: 28px;
}
.label {
display: inline-table;
padding: 1px;
padding-left: 7px;
padding-right: 7px;
border-radius: 5px;
display: flex;
align-items: center;
gap: 5px;
border: 1px solid rgb(222, 222, 222);
background-color: rgb(249, 249, 249);
}
#omnivore-toast-container .omnivore-toast-func-row .label button {
height: unset;
}
.label[data-label-backspaced="on"] {
border: 1px solid rgb(255, 234, 159);
}
.label[data-item-highlighted="on"] {
border: 1px solid black;
transition: border-color 0.25s linear;
}
</style>
<div id="omnivore-toast-container">
<div id="omnivore-toast-button-row">
@ -421,14 +506,16 @@
</form>
</div>
<div id="omnivore-edit-labels-row" class="omnivore-toast-func-row" data-state="closed">
<span id="omnivore-edit-labels-status" class="omnivore-toast-func-status"></span>
<form id="omnivore-edit-labels-form">
<input type="text" id="omnivore-edit-label-text" placeholder="Filter for labels" tabindex="0"> </input>
<div id="omnivore-edit-labels-list">
<div id="omnivore-edit-label-editor" class="label-editor">
<ul id="label-list" class="label-list">
<li id="label-entry-item">
<input type="text" id="omnivore-edit-label-input" placeholder="Add a label..." maxlength="48" class="label-input">
</li>
</ul>
</div>
<button class="omnivore-save-button">Save</button>
<span id="omnivore-edit-labels-status" class="omnivore-toast-func-status"></span>
<div id="omnivore-edit-labels-list"></div>
</form>
</div>