Compare commits

..

2 Commits

Author SHA1 Message Date
151eac9d81 Relax line height in the samples 2025-03-03 14:24:04 +01:00
d558b38cd7 Add dark color-scheme for samples 2025-03-03 14:23:31 +01:00
7 changed files with 64 additions and 374 deletions

View File

@@ -91,8 +91,8 @@ Kategorije pravopisnih pravil so:
Privzeto servis uporablja podčrtovanje pravopisnih napak (nastavitev `'underline'`). Videz lahko spreminjamo. Privzeto servis uporablja podčrtovanje pravopisnih napak (nastavitev `'underline'`). Videz lahko spreminjamo.
<img src="samples/images/markup_underline.png" alt="underline" width="448"/> <img src="samples/markup_underline.png" alt="underline" width="448"/>
<img src="samples/images/markup_lector.png" alt="lector" width="448"/> <img src="samples/markup_lector.png" alt="lector" width="448"/>
Levo `'underline'`, desno `'lector'`. Levo `'underline'`, desno `'lector'`.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 201 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 179 KiB

BIN
samples/markup_lector.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

View File

@@ -1,5 +1,4 @@
// TODO: Research if there is a way to disable languageTool & Grammarly extensions in CKEditor // TODO: Research if there is a way to disable languageTool & Grammarly extensions in CKEditor
// TODO: Add mutation observer should any style of hostElement/textElement change and repaint markup (e.g. notice font-weight difference when toggling light/dark color-scheme)
/** /**
* Collection of all grammar checking services in the document * Collection of all grammar checking services in the document
@@ -63,8 +62,6 @@ class BesService {
this.onScroll = this.onScroll.bind(this) this.onScroll = this.onScroll.bind(this)
this.hostElement.addEventListener('scroll', this.onScroll) this.hostElement.addEventListener('scroll', this.onScroll)
this.onShortcutNavigation = this.onShortcutNavigation.bind(this)
this.hostElement.addEventListener('keydown', this.onShortcutNavigation)
this.hostBoundingClientRect = this.hostElement.getBoundingClientRect() this.hostBoundingClientRect = this.hostElement.getBoundingClientRect()
this.mutationObserver = new MutationObserver(this.onBodyMutate.bind(this)) this.mutationObserver = new MutationObserver(this.onBodyMutate.bind(this))
@@ -111,7 +108,6 @@ class BesService {
if (this.abortController) this.abortController.abort() if (this.abortController) this.abortController.abort()
besServices = besServices.filter(item => item !== this) besServices = besServices.filter(item => item !== this)
this.mutationObserver.disconnect() this.mutationObserver.disconnect()
this.hostElement.removeEventListener('keydown', this.onShortcutNavigation)
this.hostElement.removeEventListener('scroll', this.onScroll) this.hostElement.removeEventListener('scroll', this.onScroll)
this.hostElement.setAttribute('spellcheck', this.originalSpellcheck) this.hostElement.setAttribute('spellcheck', this.originalSpellcheck)
this.hostElement.setAttribute('data-gramm', this.originalDataGramm) this.hostElement.setAttribute('data-gramm', this.originalDataGramm)
@@ -279,9 +275,6 @@ class BesService {
*/ */
onProofingProgress(numberOfMatches) { onProofingProgress(numberOfMatches) {
this.proofingMatches += numberOfMatches this.proofingMatches += numberOfMatches
// Sorting the array here is preferable to sorting only in onEndProofing.This way it allows users to interact
// with and navigate newly detected mistakes as soon as they appear.
this.sortMatchesArray()
if (this.eventSink && 'proofingProgress' in this.eventSink) if (this.eventSink && 'proofingProgress' in this.eventSink)
this.eventSink.proofingProgress(this) this.eventSink.proofingProgress(this)
if (--this.proofingCount <= 0) this.onEndProofing() if (--this.proofingCount <= 0) this.onEndProofing()
@@ -330,46 +323,6 @@ class BesService {
} }
} }
/**
* Called to report keydown event
*
* @param {Event} e
*/
onShortcutNavigation(e) {
switch (e.code) {
case 'BracketLeft':
if (e.ctrlKey) {
// Handle Ctrl + [ OR Ctrl + Š
e.preventDefault()
e.stopPropagation()
BesService.findNextMistake(this, -1)
}
break
case 'BracketRight':
if (e.ctrlKey) {
// Handle Ctrl + ] OR Ctrl + Đ
e.preventDefault()
e.stopPropagation()
BesService.findNextMistake(this, 1)
}
break
case 'Enter':
// Handle Ctrl + Enter
if (e.ctrlKey) {
if (!this.highlightElements.length) return
e.preventDefault()
e.stopPropagation()
this.acceptReplacement()
}
break
case 'Escape':
this.dismissPopup()
break
default:
break
}
}
/** /**
* Called to report repositioning * Called to report repositioning
*/ */
@@ -417,24 +370,13 @@ class BesService {
if (match.highlights.length === 0) return if (match.highlights.length === 0) return
const dpr = window.devicePixelRatio const dpr = window.devicePixelRatio
this.ctx.lineWidth = 2 * dpr // Use 2 for clearer visibility this.ctx.lineWidth = 2 * dpr // Use 2 for clearer visibility
let amplitude = 0
const ruleId = match.match.rule.id const ruleId = match.match.rule.id
if (ruleId.startsWith('MORFOLOGIK_RULE')) { this.ctx.strokeStyle = ruleId.startsWith('MORFOLOGIK_RULE')
const styles = window.getComputedStyle(this.highlightSpelling) ? 'rgba(0, 123, 255, 0.8)'
this.ctx.strokeStyle = styles.color : 'rgba(255, 115, 0, 0.8)'
this.ctx.fillStyle = styles.color this.ctx.fillStyle = ruleId.startsWith('MORFOLOGIK_RULE')
amplitude = -1 ? 'rgba(0, 123, 255, 0.8)'
} else if (ruleId === 'BESANA_178' /*PR_VNAP_POPRAVEK_UI*/) { : 'rgba(255, 115, 0, 0.8)'
const styles = window.getComputedStyle(this.highlightAI)
this.ctx.strokeStyle = styles.color
this.ctx.fillStyle = styles.color
amplitude = 1
} else {
const styles = window.getComputedStyle(this.highlightGrammar)
this.ctx.strokeStyle = styles.color
this.ctx.fillStyle = styles.color
amplitude = 1
}
let markerY1, markerY2 let markerY1, markerY2
switch (this.markupStyle) { switch (this.markupStyle) {
case 'lector': case 'lector':
@@ -738,9 +680,7 @@ class BesService {
const x2 = rect.right const x2 = rect.right
const y = rect.bottom const y = rect.bottom
const scale = (rect.bottom - rect.top) / 18 const scale = (rect.bottom - rect.top) / 18
if (ruleId !== 'MORFOLOGIK_RULE') { this.drawAttentionRequired(x1, x2, y, scale)
this.drawDoubleUnderline(x1, x2, y, scale)
} else this.drawAttentionRequired(x1, x2, y, amplitude, scale)
} }
markerY1 = Math.min(...match.highlights.map(rect => rect.top)) markerY1 = Math.min(...match.highlights.map(rect => rect.top))
@@ -924,50 +864,31 @@ class BesService {
* @param {Number} x1 Sign left [px] * @param {Number} x1 Sign left [px]
* @param {Number} x2 Sign right [px] * @param {Number} x2 Sign right [px]
* @param {Number} y Sign baseline [px] * @param {Number} y Sign baseline [px]
* @param {Number} amplitude Sign amplitude [px]
* @param {Number} scale Sign scale * @param {Number} scale Sign scale
*/ */
drawAttentionRequired(x1, x2, y, amplitude, scale) { drawAttentionRequired(x1, x2, y, scale) {
const dpr = window.devicePixelRatio const dpr = window.devicePixelRatio
this.ctx.beginPath() this.ctx.beginPath()
this.ctx.moveTo(x1 * dpr, (y - amplitude * scale) * dpr) this.ctx.moveTo(x1 * dpr, (y - scale) * dpr)
for (let x = x1; ; ) { for (let x = x1; ; ) {
if (x >= x2) break if (x >= x2) break
this.ctx.lineTo((x += 2 * scale) * dpr, (y + amplitude * scale) * dpr) this.ctx.lineTo((x += 2 * scale) * dpr, (y + scale) * dpr)
if (x >= x2) break if (x >= x2) break
this.ctx.lineTo((x += 2 * scale) * dpr, (y - amplitude * scale) * dpr) this.ctx.lineTo((x += 2 * scale) * dpr, (y - scale) * dpr)
} }
this.ctx.stroke() this.ctx.stroke()
} }
/**
*
* @param {Number} x1 Sign left [px]
* @param {Number} x2 Sign right [px]
* @param {Number} y Sign baseline [px]
* @param {Number} scale Sign scale
*/
drawDoubleUnderline(x1, x2, y, scale) {
const dpr = window.devicePixelRatio
this.ctx.beginPath()
this.ctx.moveTo(x1 * dpr, (y - 2 * scale) * dpr)
this.ctx.lineTo(x2 * dpr, (y - 2 * scale) * dpr)
this.ctx.moveTo(x1 * dpr, (y + 1 * scale) * dpr)
this.ctx.lineTo(x2 * dpr, (y + 1 * scale) * dpr)
this.ctx.stroke()
}
/** /**
* Sets markup font * Sets markup font
* *
* @param {Number} scale Sign scale * @param {Number} scale Sign scale
* @param {Number} dpr Device pixel ratio * @param {Number} dpr Device pixel ratio
*/ */
setCtxFont(scale, dpr) { setCtxFont(scale, dpr)
{
const styles = window.getComputedStyle(this.canvasPanel) const styles = window.getComputedStyle(this.canvasPanel)
this.ctx.font = `${styles.fontStyle} ${styles.fontWeight} ${ this.ctx.font = `${styles.fontStyle} ${styles.fontWeight} ${14 * scale * dpr}px ${styles.fontFamily}`
14 * scale * dpr
}px ${styles.fontFamily}`
} }
/** /**
@@ -1022,21 +943,10 @@ class BesService {
* @param {Number} x X coordinate * @param {Number} x X coordinate
* @param {Number} y Y coordinate * @param {Number} y Y coordinate
* @param {DOMRect} rect Rectangle * @param {DOMRect} rect Rectangle
* @param {Number} tolerance Extra margin around the rectangle treated as "inside"
* @returns * @returns
*/ */
static isPointInRect(x, y, rect, tolerance) { static isPointInRect(x, y, rect) {
return ( return rect.left <= x && x < rect.right && rect.top <= y && y < rect.bottom
rect.left - tolerance <= x &&
x < rect.right + tolerance &&
rect.top - tolerance <= y &&
y < rect.bottom + tolerance
)
}
static arrowBtnNavigation(value, service) {
const direction = value === 'forward' ? 1 : value === 'back' ? -1 : 0
BesService.findNextMistake(service, direction)
} }
/** /**
@@ -1054,20 +964,7 @@ class BesService {
this.ctx = this.canvasPanel.getContext('2d') this.ctx = this.canvasPanel.getContext('2d')
this.ctx.scale(1, 1) this.ctx.scale(1, 1)
this.highlightSpelling = document.createElement('div')
this.highlightSpelling.classList.add('bes-highlight-placeholder')
this.highlightSpelling.classList.add('bes-highlight-spelling')
this.highlightAI = document.createElement('div')
this.highlightAI.classList.add('bes-highlight-placeholder')
this.highlightAI.classList.add('bes-highlight-ai')
this.highlightGrammar = document.createElement('div')
this.highlightGrammar.classList.add('bes-highlight-placeholder')
this.highlightGrammar.classList.add('bes-highlight-grammar')
this.correctionPanel.appendChild(this.scrollPanel) this.correctionPanel.appendChild(this.scrollPanel)
this.correctionPanel.appendChild(this.highlightSpelling)
this.correctionPanel.appendChild(this.highlightAI)
this.correctionPanel.appendChild(this.highlightGrammar)
this.scrollPanel.appendChild(this.canvasPanel) this.scrollPanel.appendChild(this.canvasPanel)
this.textElement.parentElement.insertBefore( this.textElement.parentElement.insertBefore(
this.correctionPanel, this.correctionPanel,
@@ -1135,24 +1032,18 @@ class BesService {
/** /**
* Prepares and displays popup. * Prepares and displays popup.
* *
* @param {*} hits Array containing block element/paragraph containing grammar checking rule match and a match * @param {*} elMatch Array containing block element/paragraph containing grammar checking rule match and a match
* @param {PointerEvent} source Click event source * @param {PointerEvent} source Click event source
*/ */
preparePopup(hits, source) { preparePopup(elMatch, source) {
this.dismissPopup() this.dismissPopup()
const popup = document.querySelector('bes-popup-el') const popup = document.querySelector('bes-popup-el')
BesPopup.clearReplacements() BesPopup.clearReplacements()
hits.forEach(({ el, match }) => { elMatch.forEach(({ el, match }) => {
popup.setContent(el, match, this, this.isContentEditable()) popup.setContent(el, match, this, this.isContentEditable())
this.highlightMistake(match) this.highlightMistake(match)
const containerRect = this.hostElement.getBoundingClientRect()
match.highlights.forEach(rect => {
const clientX = rect.x + containerRect.left
const clientY =
rect.y + containerRect.top + rect.height - this.hostElement.scrollTop
popup.show(clientX, clientY, this)
})
}) })
popup.show(source.clientX, source.clientY)
} }
/** /**
@@ -1167,123 +1058,27 @@ class BesService {
el.classList.add('bes-highlight-rect') el.classList.add('bes-highlight-rect')
el.classList.add( el.classList.add(
match.match.rule.id.startsWith('MORFOLOGIK_RULE') match.match.rule.id.startsWith('MORFOLOGIK_RULE')
? 'bes-highlight-spelling' ? 'bes-highlight-spelling-rect'
: match.match.rule.id === 'BESANA_178' /*PR_VNAP_POPRAVEK_UI*/ : 'bes-highlight-grammar-rect'
? 'bes-highlight-ai'
: 'bes-highlight-grammar'
) )
el.style.left = `${rect.x + canvasPanelRect.x + window.scrollX}px` el.style.left = `${rect.x + canvasPanelRect.x + window.scrollX}px`
el.style.top = `${rect.y + canvasPanelRect.y + window.scrollY}px` el.style.top = `${rect.y + canvasPanelRect.y + window.scrollY}px`
el.style.width = `${rect.width}px` el.style.width = `${rect.width}px`
el.style.height = `${rect.height}px` el.style.height = `${rect.height}px`
document.body.appendChild(el) document.body.appendChild(el)
const matchSorted = this.highlightElements.push(el)
this.sortedMatches.find(entry => entry.match === match) || null
this.highlightElements.push({ el, matchSorted })
}) })
} }
/**
* This function finds the next/previous mistake.
* @param {Number} direction Navigation direction: 1 for next, -1 for previous
* @returns
*/
static findNextMistake(service, direction = 1) {
if (!service || !service.sortedMatches || !service.sortedMatches.length)
return
const active = service.highlightElements.find(
({ matchSorted }) => matchSorted
)
let current = -1
if (active && active.matchSorted) {
current = service.sortedMatches.findIndex(
entry => entry.match === active.matchSorted.match
)
}
const len = service.sortedMatches.length
const next = (current + direction + len) % len
service.activeMatchIndex = next
const { el, match } = service.sortedMatches[next]
if (el && typeof el.scrollIntoView === 'function') {
el.scrollIntoView({ behavior: 'instant', block: 'center' })
}
// Not the cleanest solution to setTimeout()
setTimeout(() => {
service.dismissPopup()
const popup = document.querySelector('bes-popup-el')
BesPopup.clearReplacements()
popup.setContent(el, match, service, service.isContentEditable())
service.highlightMistake(match)
const containerRect = service.hostElement.getBoundingClientRect()
match.highlights.forEach(rect => {
const clientX = rect.x + containerRect.left
const clientY =
rect.y +
containerRect.top +
rect.height -
service.hostElement.scrollTop
popup.show(clientX, clientY, service)
})
}, 150)
}
/**
* Accepts the replacement for the current grammar mistake.
*/
acceptReplacement() {
const popup = document.querySelector('bes-popup-el')
const replacementDiv = popup.shadowRoot.querySelector(
'.bes-replacement-div'
)
const firstReplacement = replacementDiv?.firstChild
if (replacementDiv.childElementCount === 1) {
// Is this an ugly solution?
firstReplacement.click()
} else if (replacementDiv.childElementCount > 1) {
firstReplacement.focus()
} else BesService.findNextMistake(this, 1)
}
/** /**
* Clears highlight and hides popup * Clears highlight and hides popup
*/ */
dismissPopup() { dismissPopup() {
BesPopup.hide() BesPopup.hide()
this.highlightElements.forEach(obj => { this.highlightElements.forEach(el => el.remove())
if (obj.el && typeof obj.el.remove === 'function') {
obj.el.remove()
}
})
this.highlightElements = [] this.highlightElements = []
} }
/**
* This function collects all matches from the results array, flattens them into a single array,
* and sorts them in order: first by their Y axis, then by X axis.
*/
sortMatchesArray() {
this.sortedMatches = []
this.results.forEach(element => {
element.matches.forEach(match => {
if (!match.highlights || !match.highlights.length) return
this.sortedMatches.push({
el: element.element,
match,
top: match.highlights[0].top
})
})
})
this.sortedMatches.sort((a, b) => {
if (a.top !== b.top) return a.top - b.top
const aLeft = a.match.highlights[0].left
const bLeft = b.match.highlights[0].left
return aLeft - bLeft
})
}
/** /**
* Checks if host element content is editable. * Checks if host element content is editable.
* *
@@ -1304,9 +1099,7 @@ class BesService {
redrawAllMistakeMarkup() { redrawAllMistakeMarkup() {
this.ctx.clearRect(0, 0, this.canvasPanel.width, this.canvasPanel.height) this.ctx.clearRect(0, 0, this.canvasPanel.width, this.canvasPanel.height)
this.results.forEach(result => { this.results.forEach(result => {
// Most important matches are first, we want to draw them last => iterate in reverse. result.matches.forEach(match => this.drawMistakeMarkup(match))
for (let i = result.matches.length; i-- > 0; )
this.drawMistakeMarkup(result.matches[i])
}) })
} }
@@ -1462,11 +1255,9 @@ class BesTreeService extends BesService {
), ),
match: match match: match
} }
this.drawMistakeMarkup(m)
matches.push(m) matches.push(m)
}) })
// Most important matches are first, we want to draw them last => iterate in reverse.
for (let i = matches.length; i-- > 0; )
this.drawMistakeMarkup(matches[i])
this.markProofed(node, matches) this.markProofed(node, matches)
this.onProofingProgress(matches.length) this.onProofingProgress(matches.length)
}) })
@@ -1722,19 +1513,19 @@ class BesTreeService extends BesService {
const canvasPanelRect = this.canvasPanel.getBoundingClientRect() const canvasPanelRect = this.canvasPanel.getBoundingClientRect()
let x = source.clientX - canvasPanelRect.x let x = source.clientX - canvasPanelRect.x
let y = source.clientY - canvasPanelRect.y let y = source.clientY - canvasPanelRect.y
const hits = [] const pointsInRect = []
for (let result of this.results) { for (let result of this.results) {
for (let m of result.matches) { for (let m of result.matches) {
for (let rect of m.highlights) { for (let rect of m.highlights) {
if (BesService.isPointInRect(x, y, rect, 5)) { if (BesService.isPointInRect(x, y, rect)) {
hits.push({ el, match: m }) pointsInRect.push({ el, match: m })
break break
} }
} }
} }
} }
this.dismissPopup() this.dismissPopup()
if (hits.length) this.preparePopup(hits, source) if (pointsInRect.length) this.preparePopup(pointsInRect, source)
} }
} }
@@ -2038,7 +1829,6 @@ class BesQuillService extends BesTreeService {
onChangeData(delta) { onChangeData(delta) {
let index = 0 let index = 0
let reproofNeeded = false let reproofNeeded = false
const affectedBlocks = new Set()
delta.ops.forEach(op => { delta.ops.forEach(op => {
if (op.retain) { if (op.retain) {
@@ -2048,12 +1838,11 @@ class BesQuillService extends BesTreeService {
} }
} else if (op.insert) { } else if (op.insert) {
reproofNeeded = true reproofNeeded = true
index += typeof op.insert === 'string' ? op.insert.length : 1 // Handle string or embed index += op.insert.length
} else if (op.delete) { } else if (op.delete) {
reproofNeeded = true reproofNeeded = true
} }
}) })
if (reproofNeeded) { if (reproofNeeded) {
const editorLength = this.quillInstance.getLength() const editorLength = this.quillInstance.getLength()
const clampedIndex = Math.min(index, editorLength - 1) const clampedIndex = Math.min(index, editorLength - 1)
@@ -2062,55 +1851,22 @@ class BesQuillService extends BesTreeService {
if (leaf) { if (leaf) {
let domElement = leaf.domNode let domElement = leaf.domNode
// Traverse up to find the block element
while (domElement && !this.isBlockElement(domElement)) { while (domElement && !this.isBlockElement(domElement)) {
domElement = domElement.parentNode domElement = domElement.parentNode
} }
if (domElement) {
this.clearProofing(domElement)
if (domElement) affectedBlocks.add(domElement) setTimeout(() => {
this.redrawAllMistakeMarkup()
this.scheduleProofing(1000)
}, 0)
}
} else { } else {
console.warn( console.warn(
'Leaf is null. The index might be out of bounds or the editor content is empty.' 'Leaf is null. The index might be out of bounds or the editor content is empty.'
) )
} }
// Handle pasted content spanning multiple blocks
const selection = this.quillInstance.getSelection()
if (selection) {
const [startLeaf] = this.quillInstance.getLeaf(selection.index)
const [endLeaf] = this.quillInstance.getLeaf(
selection.index + selection.length
)
if (startLeaf && endLeaf) {
let startElement = startLeaf.domNode
let endElement = endLeaf.domNode
while (startElement && !this.isBlockElement(startElement)) {
startElement = startElement.parentNode
}
while (endElement && !this.isBlockElement(endElement)) {
endElement = endElement.parentNode
}
if (startElement && endElement) {
let currentElement = startElement
while (currentElement) {
affectedBlocks.add(currentElement)
if (currentElement === endElement) break
currentElement = currentElement.nextElementSibling
}
}
}
}
// Clear proofing for all affected blocks
affectedBlocks.forEach(block => this.clearProofing(block))
// Schedule proofing for all affected blocks
setTimeout(() => {
this.scheduleProofing(1000)
}, 0)
} }
} }
@@ -2288,11 +2044,9 @@ class BesPlainTextService extends BesService {
), ),
match: match match: match
} }
this.drawMistakeMarkup(m)
matches.push(m) matches.push(m)
}) })
// Most important matches are first, we want to draw them last => iterate in reverse.
for (let i = matches.length; i-- > 0; )
this.drawMistakeMarkup(matches[i])
this.markProofed(paragraphRange, matches) this.markProofed(paragraphRange, matches)
this.onProofingProgress(matches.length) this.onProofingProgress(matches.length)
}) })
@@ -2427,19 +2181,19 @@ class BesPlainTextService extends BesService {
const canvasPanelRect = this.canvasPanel.getBoundingClientRect() const canvasPanelRect = this.canvasPanel.getBoundingClientRect()
let x = source.clientX - canvasPanelRect.x let x = source.clientX - canvasPanelRect.x
let y = source.clientY - canvasPanelRect.y let y = source.clientY - canvasPanelRect.y
const hits = [] const pointsInRect = []
for (let result of this.results) { for (let result of this.results) {
for (let m of result.matches) { for (let m of result.matches) {
for (let rect of m.highlights) { for (let rect of m.highlights) {
if (BesService.isPointInRect(x, y, rect, 5)) { if (BesService.isPointInRect(x, y, rect)) {
hits.push({ el: result.range, match: m }) pointsInRect.push({ el: result.range, match: m })
break break
} }
} }
} }
} }
this.dismissPopup() this.dismissPopup()
if (hits.length) this.preparePopup(hits, source) if (pointsInRect.length) this.preparePopup(pointsInRect, source)
} }
/** /**
@@ -2712,13 +2466,11 @@ class BesTAService extends BesPlainTextService {
parseFloat(styles.paddingRight) - parseFloat(styles.paddingRight) -
parseFloat(styles.borderRightWidth) parseFloat(styles.borderRightWidth)
}px` }px`
textElement.style.height = `${ textElement.style.height = `${rect.height -
rect.height -
parseFloat(styles.borderTopWidth) - parseFloat(styles.borderTopWidth) -
parseFloat(styles.paddingTop) - parseFloat(styles.paddingTop) -
parseFloat(styles.paddingBottom) - parseFloat(styles.paddingBottom) -
parseFloat(styles.borderBottomWidth) parseFloat(styles.borderBottomWidth)}px`
}px`
} }
/** /**
@@ -2916,7 +2668,7 @@ class BesPopup extends HTMLElement {
padding: 3px 2px; padding: 3px 2px;
} }
.bes-toolbar button { .bes-toolbar button {
margin-right: 0px; margin-right: 2px;
} }
.bes-popup-title { .bes-popup-title {
color: #333; color: #333;
@@ -2941,12 +2693,6 @@ class BesPopup extends HTMLElement {
} }
.bes-replacement-btn:hover{ .bes-replacement-btn:hover{
background-color: #1976f0; background-color: #1976f0;
}
.bes-replacement-btn:focus{
outline: -webkit-focus-ring-color auto 1px;
}
.bes-replacement-btn:focus-visible{
outline: -webkit-focus-ring-color auto 1px;
} }
.bes-replacement-div{ .bes-replacement-div{
margin-top: 4px; margin-top: 4px;
@@ -2962,17 +2708,11 @@ class BesPopup extends HTMLElement {
.bes-close-btn svg { .bes-close-btn svg {
width: 100%; width: 100%;
height: 100%; height: 100%;
fill: #333;
} }
.bes-mistake-nav{
margin-right: 10px;
}
:host{
--bes-close-icon: #485362;
--hover-bg-clr: #dee3ed;
}
.bes-close-btn:hover { .bes-close-btn:hover {
background: var(--hover-bg-clr); background: #dee3ed;
border-radius: 4px border-radius: 8px
} }
:host(.show) .bes-popup-container { :host(.show) .bes-popup-container {
visibility: visible; visibility: visible;
@@ -3001,45 +2741,17 @@ class BesPopup extends HTMLElement {
background-color: #111213; background-color: #111213;
border: 1px solid #2e3036; border: 1px solid #2e3036;
} }
:host{
--bes-close-icon: #a4b5c7;
--hover-bg-clr:rgba(189, 189, 189, 0.28);
}
} }
</style> </style>
<div class="bes-popup-container"> <div class="bes-popup-container">
<div class="bes-toolbar"> <div class="bes-toolbar">
<div class="bes-popup-title">Besana</div> <div class="bes-popup-title">Besana</div>
<div class="bes-mistake-nav">
<button class="bes-close-btn" title="Prejšnja napaka">
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><!-- Icon from Material Symbols by Google - https://github.com/google/material-design-icons/blob/master/LICENSE --><path fill="var(--bes-close-icon)" d="M11 20V7.825l-5.6 5.6L4 12l8-8l8 8l-1.4 1.425l-5.6-5.6V20z"/></svg>
</button>
<button class="bes-close-btn" title="Naslednja napaka">
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><!-- Icon from Material Symbols by Google - https://github.com/google/material-design-icons/blob/master/LICENSE --><path fill="var(--bes-close-icon)" d="M11 4v12.175l-5.6-5.6L4 12l8 8l8-8l-1.4-1.425l-5.6 5.6V4z"/></svg>
</button>
</div>
<button class="bes-close-btn" onclick="BesPopup.dismiss()"> <button class="bes-close-btn" onclick="BesPopup.dismiss()">
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><path fill="var(--bes-close-icon)" d="M13.46 12L19 17.54V19h-1.46L12 13.46L6.46 19H5v-1.46L10.54 12L5 6.46V5h1.46L12 10.54L17.54 5H19v1.46z"/></svg> <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><path fill="currentColor" d="M13.46 12L19 17.54V19h-1.46L12 13.46L6.46 19H5v-1.46L10.54 12L5 6.46V5h1.46L12 10.54L17.54 5H19v1.46z"/></svg>
</button> </button>
</div> </div>
</div> </div>
` `
const prevBtn = this.shadowRoot.querySelector(
'.bes-mistake-nav .bes-close-btn[title="Prejšnja napaka"]'
)
const nextBtn = this.shadowRoot.querySelector(
'.bes-mistake-nav .bes-close-btn[title="Naslednja napaka"]'
)
prevBtn.addEventListener('click', () => {
if (this.hostElService)
BesService.arrowBtnNavigation('back', this.hostElService)
})
nextBtn.addEventListener('click', () => {
if (this.hostElService)
BesService.arrowBtnNavigation('forward', this.hostElService)
})
this.addEventListener('mousedown', this.onMouseDown) this.addEventListener('mousedown', this.onMouseDown)
} }
@@ -3049,9 +2761,8 @@ class BesPopup extends HTMLElement {
* @param {Number} x X location hint * @param {Number} x X location hint
* @param {Number} y Y location hint * @param {Number} y Y location hint
*/ */
show(x, y, service) { show(x, y) {
this.style.position = 'fixed' this.style.position = 'fixed'
this.hostElService = service
// Element needs some initial placement for the browser to provide this.offsetWidth and this. // Element needs some initial placement for the browser to provide this.offsetWidth and this.
// offsetHeight measurements. // offsetHeight measurements.

View File

@@ -1,39 +1,23 @@
/* Mistake types styles */ /* Mistake types styles */
.bes-highlight-rect { .bes-spelling-mistake {
border-bottom: 2px solid #ff7300;
position: absolute; position: absolute;
z-index: 3;
cursor: text; cursor: text;
} }
.bes-highlight-spelling { .bes-highlight-rect {
color: rgb(242, 90, 90); position: absolute;
background: hsla(0, 100%, 67%, 0.18); opacity: 0.3;
cursor: text;
} }
.bes-highlight-ai { .bes-highlight-spelling-rect {
color: rgb(139, 62, 223); background: rgb(0, 123, 255);
background: hsla(262, 70%, 56%, 0.18);
} }
.bes-highlight-grammar { .bes-highlight-grammar-rect {
color: rgb(60, 120, 220); background: rgb(255, 115, 0);
background: hsla(220, 80%, 56%, 0.18);
}
@media (prefers-color-scheme: dark) {
.bes-highlight-spelling {
color: rgb(255, 120, 120);
background: hsla(0, 100%, 67%, 0.32);
}
.bes-highlight-ai {
color: rgb(180, 120, 255);
background: hsla(262, 70%, 56%, 0.32);
}
.bes-highlight-grammar {
color: rgb(100, 164, 243);
background: hsla(220, 80%, 56%, 0.32);
}
} }
/* Styles required to ensure full functionality and optimal user experience. */ /* Styles required to ensure full functionality and optimal user experience. */
@@ -70,8 +54,3 @@
background: none; background: none;
z-index: -1; z-index: -1;
} }
.bes-highlight-placeholder {
display: none;
visibility: hidden;
}