Skip to content

Commit

Permalink
Renamed popups for clarity
Browse files Browse the repository at this point in the history
And renamed init_flags to initFlags
  • Loading branch information
haukex committed Aug 11, 2024
1 parent 5e74ffd commit ec63773
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 70 deletions.
10 changes: 5 additions & 5 deletions src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,15 @@ <h1>German-English Dictionary</h1>
</figure>
<p id="result-count">The dictionary is loading, please wait...</p>

<div id="abbr-tooltip" class="popper d-none">...</div>
<div id="title-tooltip" class="popper d-none">...</div>

<div id="sel-popup" class="popper d-none">
<div id="sel-tools" class="popper d-none">
<div>
<div id="popup-close" title="Close"></div>
<div id="sel-tools-close" title="Close"></div>
Selection tools:
</div>
<a id="popup-search" href="#q=" title="Search for the selected text">🔍 Search</a>
<a id="popup-feedback" href="#" title="Send Feeedback Email">✉️ Feedback</a>
<a id="sel-tools-search" href="#q=" title="Search for the selected text">🔍 Search</a>
<a id="sel-tools-feedback" href="#" title="Send Feeedback Email">✉️ Feedback</a>
</div>

<details lang="de">
Expand Down
2 changes: 1 addition & 1 deletion src/js/flags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const FLAGS :IFlagList = { en: [ '🇺🇸', '🇬🇧', '🇦🇺', '🇳🇿'

const INTERVAL_MS = 4000

export function init_flags () {
export function initFlags () {
['de', 'en'].forEach(key => {
const element = document.getElementById('flag-'+key) as HTMLElement
// add the other flag <div>s
Expand Down
15 changes: 7 additions & 8 deletions src/js/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@
*/

import {DB_URL, DB_VER_URL, DB_CACHE_NAME, cacheFirst, cleanSearchTerm} from './common'
import {init_flags} from './flags'
import {initFlags} from './flags'
import {makeSearchPattern} from './equiv'
import {initPopup, initTooltips, addTooltips, closeAllPopups} from './popup'
import {initPopups, addTitleTooltips, closeAllPopups} from './popups'
import {default as abbreviations} from './abbreviations.json'

// for the parcel development environment:
Expand Down Expand Up @@ -221,11 +221,10 @@ window.addEventListener('DOMContentLoaded', async () => {
console.debug(`Loaded ${dictLines.length} dictionary lines`)
search_term.removeAttribute('disabled')

// set up flag animations and selection popup handler
// set up flag animations and popups
try {
init_flags()
initPopup()
initTooltips()
initFlags()
initPopups()
}
// but don't let bugs blow us up
catch (error) { console.error(error) }
Expand Down Expand Up @@ -350,7 +349,7 @@ window.addEventListener('DOMContentLoaded', async () => {
td.innerHTML = td.innerHTML.replaceAll(whatRe, '<strong>$&</strong>')
})
result_table.appendChild(tbody)
addTooltips(tbody)
addTitleTooltips(tbody.querySelectorAll('abbr'))
displayedMatches++
}
catch (error) { console.error(error) }
Expand Down Expand Up @@ -391,7 +390,7 @@ window.addEventListener('DOMContentLoaded', async () => {
clearResults()
const tbody = result2tbody( dictLines[Math.floor(Math.random()*dictLines.length)] as string )
result_table.appendChild(tbody)
addTooltips(tbody)
addTitleTooltips(tbody.querySelectorAll('abbr'))
})

search_term.addEventListener('keyup', event => {
Expand Down
114 changes: 60 additions & 54 deletions src/js/popup.ts → src/js/popups.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,83 +23,78 @@

import {computePosition, autoUpdate, shift, flip, limitShift, offset} from '@floating-ui/dom'

/* Handles the "Selection Tools" popup and the "Annotation Tooltips".
* The former is currently abbreviated "popup" and the second "tooltip".
* That's probably too generic and confusing and I should rename them. */
/* Handles the "Selection Tools" Popup and Tooltips for Elements with "title"s (such as <abbr>). */

let cleanupPopup :null|(()=>void) = null
let cleanupTooltip :null|(()=>void) = null
let currentTooltipElement :Node|null = null
let cleanupSelectionTools :null|(()=>void) = null
let cleanupTitleTooltip :null|(()=>void) = null
let titleTooltipSource :Node|null = null

function closeTooltip() {
if (cleanupTooltip) {
const abbr_tooltip = document.getElementById('abbr-tooltip') as HTMLElement
abbr_tooltip.classList.add('d-none')
cleanupTooltip()
cleanupTooltip = null
currentTooltipElement = null
function closeTitleTooltip() {
if (cleanupTitleTooltip) {
const title_tooltip = document.getElementById('title-tooltip') as HTMLElement
title_tooltip.classList.add('d-none')
cleanupTitleTooltip()
cleanupTitleTooltip = null
titleTooltipSource = null
}
}

function closePopup() {
if (cleanupPopup) {
cleanupPopup()
const sel_popup = document.getElementById('sel-popup') as HTMLElement
sel_popup.classList.add('d-none')
cleanupPopup = null
function closeSelectionTools() {
if (cleanupSelectionTools) {
cleanupSelectionTools()
const sel_tools = document.getElementById('sel-tools') as HTMLElement
sel_tools.classList.add('d-none')
cleanupSelectionTools = null
}
}

export function closeAllPopups() {
closePopup()
closeTooltip()
}

export function initTooltips() {
const abbr_tooltip = document.getElementById('abbr-tooltip') as HTMLElement
function initTitleTooltips() {
const title_tooltip = document.getElementById('title-tooltip') as HTMLElement
document.addEventListener('click', (event) => {
// clicking anywhere but the tooltip or the element that opened it closes it
if ( !( abbr_tooltip.contains(event.target as Node) || currentTooltipElement && currentTooltipElement.contains(event.target as Node) ) )
closeTooltip()
if ( !( title_tooltip.contains(event.target as Node) || titleTooltipSource && titleTooltipSource.contains(event.target as Node) ) )
closeTitleTooltip()
})
}

export function addTooltips(element :HTMLElement) {
export function addTitleTooltips(elements :NodeListOf<HTMLElement>) {
// important: don't call this until the element is actually part of the document, otherwise event listeners won't register
const abbr_tooltip = document.getElementById('abbr-tooltip') as HTMLElement
element.querySelectorAll('abbr').forEach((el) => {
const title_tooltip = document.getElementById('title-tooltip') as HTMLElement
elements.forEach((el) => {
const title = el.getAttribute('title')
if (!title) return
el.addEventListener('click', (event) => {
if (event.detail>1 ) return // not on double clicks (those would select the text)
closeAllPopups()
abbr_tooltip.innerText = title
abbr_tooltip.classList.remove('d-none')
if (cleanupTooltip) cleanupTooltip()
cleanupTooltip = autoUpdate( el, abbr_tooltip, () => {
computePosition( el, abbr_tooltip, {
title_tooltip.innerText = title
title_tooltip.classList.remove('d-none')
cleanupTitleTooltip = autoUpdate( el, title_tooltip, () => {
computePosition( el, title_tooltip, {
placement: 'bottom-start',
middleware: [ offset(3), flip(), shift({ padding: 5, limiter: limitShift() }) ],
} ).then( ({x,y}) => {
Object.assign( abbr_tooltip.style, { left: `${x}px`, top: `${y}px` } )
Object.assign( title_tooltip.style, { left: `${x}px`, top: `${y}px` } )
} )
} )
currentTooltipElement = el
titleTooltipSource = el
} )
})
}

export function initPopup() {
const sel_popup = document.getElementById('sel-popup') as HTMLElement
const popup_search = document.getElementById('popup-search') as HTMLElement
const popup_feedback = document.getElementById('popup-feedback') as HTMLElement
function initSelectionTools() {
const sel_tools = document.getElementById('sel-tools') as HTMLElement
const sel_tools_search = document.getElementById('sel-tools-search') as HTMLElement
const sel_tools_feedback = document.getElementById('sel-tools-feedback') as HTMLElement
const sel_tools_close = document.getElementById('sel-tools-close') as HTMLElement
const result_table = document.getElementById('result-table') as HTMLElement
const popup_close = document.getElementById('popup-close') as HTMLElement

popup_close.addEventListener('click', closePopup)
sel_tools_close.addEventListener('click', closeSelectionTools)
document.addEventListener('selectionchange', () => {
const selection = window.getSelection()
closeAllPopups()
/* NOTE: Clicking inside the selection tools popup changes the selection and therefore closes it,
* unlike the title tooltips. Is this desirable behavior? It also means that providing the "x" to
* close the selection tools and explicitly adding a click listener is a little redundant. */
closeSelectionTools()
// something was selected (we only handle simple selections with one range)
if ( selection && selection.rangeCount==1 ) {
const text = selection.toString()
Expand All @@ -114,7 +109,7 @@ export function initPopup() {
let parent_elem :Node|null = range.commonAncestorContainer
while ( parent_elem && !(parent_elem instanceof HTMLElement) )
parent_elem = parent_elem.parentNode
popup_feedback.classList.add('d-none') // hide by default (gets shown below if applicable)
sel_tools_feedback.classList.add('d-none') // hide by default (gets shown below if applicable)
// figure out if the selection spans only one row
if ( parent_elem ) {
// at this point we know parent_elem must be an HTMLElement
Expand All @@ -124,24 +119,35 @@ export function initPopup() {
// get href for the feedback link that should be stored here for us
const fb = tb.getAttribute('data-feedback-href')
if (fb) {
popup_feedback.setAttribute('href', fb)
popup_feedback.classList.remove('d-none')
sel_tools_feedback.setAttribute('href', fb)
sel_tools_feedback.classList.remove('d-none')
}
}
}
// set the search link href and show the div
popup_search.setAttribute('href', `#q=${encodeURIComponent(text)}`)
sel_popup.classList.remove('d-none')
sel_tools_search.setAttribute('href', `#q=${encodeURIComponent(text)}`)
sel_tools.classList.remove('d-none')
// use Floating UI for placement
cleanupPopup = autoUpdate( range, sel_popup, () => {
computePosition( range, sel_popup, {
closeTitleTooltip()
cleanupSelectionTools = autoUpdate( range, sel_tools, () => {
computePosition( range, sel_tools, {
placement: 'bottom-start',
middleware: [ offset(5), flip(), shift({ padding: 5, limiter: limitShift() }) ],
} ).then( ({x,y}) => {
Object.assign( sel_popup.style, { left: `${x}px`, top: `${y}px` } )
Object.assign( sel_tools.style, { left: `${x}px`, top: `${y}px` } )
} )
})
}
}
})
}
}

export function initPopups() {
initSelectionTools()
initTitleTooltips()
}

export function closeAllPopups() {
closeSelectionTools()
closeTitleTooltip()
}
4 changes: 2 additions & 2 deletions src/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -171,12 +171,12 @@ tbody.result tr:not(:first-child) td {
font-size: 0.9rem;
z-index: 10;
}
#sel-popup > a {
#sel-tools > a {
display: block;
text-decoration: none;
width: max-content;
}
#popup-close {
#sel-tools-close {
float: right;
margin-left: 1rem;
cursor: pointer;
Expand Down

0 comments on commit ec63773

Please sign in to comment.