feat(member): make overview columns sortable through accessible sort headers
This commit is contained in:
parent
dfc616257d
commit
0c375234c8
9 changed files with 350 additions and 51 deletions
|
|
@ -336,6 +336,72 @@ Hooks.SortableList = {
|
|||
}
|
||||
}
|
||||
|
||||
// SortTooltip hook: shows the sort-header hint in the browser TOP LAYER via the
|
||||
// native Popover API, so it escapes the members table's overflow clipping
|
||||
// (overflow-x:auto forces overflow-y:auto, which clips a CSS tooltip below the
|
||||
// header) and paints above the sticky header, neighbor cells, and the pinned
|
||||
// checkbox column's fade — no z-index fight. The popover is shown on hover AND
|
||||
// keyboard focus and positioned next to the header with getBoundingClientRect
|
||||
// (portable; CSS anchor-positioning is not yet supported in Safari/Firefox).
|
||||
Hooks.SortTooltip = {
|
||||
mounted() {
|
||||
this.tip = document.getElementById(this.el.dataset.tooltipId)
|
||||
// Feature-detect: without Popover API support fall back to no visual tooltip
|
||||
// (the button's aria-label still conveys the hint to assistive tech).
|
||||
if (!this.tip || typeof this.tip.showPopover !== "function") return
|
||||
|
||||
this.position = () => {
|
||||
const btn = this.el.getBoundingClientRect()
|
||||
const tip = this.tip.getBoundingClientRect()
|
||||
const gap = 6
|
||||
// Prefer just below the header; flip above when there is no room below.
|
||||
let top = btn.bottom + gap
|
||||
if (top + tip.height > window.innerHeight && btn.top - gap - tip.height >= 0) {
|
||||
top = btn.top - gap - tip.height
|
||||
}
|
||||
// Left-align to the header, but keep the whole bubble on-screen.
|
||||
let left = btn.left
|
||||
const maxLeft = window.innerWidth - tip.width - gap
|
||||
if (left > maxLeft) left = maxLeft
|
||||
if (left < gap) left = gap
|
||||
this.tip.style.top = `${Math.round(top)}px`
|
||||
this.tip.style.left = `${Math.round(left)}px`
|
||||
}
|
||||
|
||||
this.show = () => {
|
||||
try {
|
||||
if (!this.tip.matches(":popover-open")) this.tip.showPopover()
|
||||
// showPopover() makes it measurable; positioning in the same tick avoids
|
||||
// a visible flash (JS runs before the browser paints).
|
||||
this.position()
|
||||
} catch (_e) {}
|
||||
}
|
||||
|
||||
this.hide = () => {
|
||||
try {
|
||||
if (this.tip.matches(":popover-open")) this.tip.hidePopover()
|
||||
} catch (_e) {}
|
||||
}
|
||||
|
||||
this.onKey = (e) => { if (e.key === "Escape") this.hide() }
|
||||
|
||||
this.el.addEventListener("mouseenter", this.show)
|
||||
this.el.addEventListener("focus", this.show)
|
||||
this.el.addEventListener("mouseleave", this.hide)
|
||||
this.el.addEventListener("blur", this.hide)
|
||||
this.el.addEventListener("keydown", this.onKey)
|
||||
},
|
||||
|
||||
destroyed() {
|
||||
if (this.hide) this.hide()
|
||||
this.el.removeEventListener("mouseenter", this.show)
|
||||
this.el.removeEventListener("focus", this.show)
|
||||
this.el.removeEventListener("mouseleave", this.hide)
|
||||
this.el.removeEventListener("blur", this.hide)
|
||||
this.el.removeEventListener("keydown", this.onKey)
|
||||
}
|
||||
}
|
||||
|
||||
// SidebarState hook: Manages sidebar expanded/collapsed state
|
||||
Hooks.SidebarState = {
|
||||
mounted() {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue