1
0
Fork 0
forked from Mirrors/gomuks

web/emojipicker: underline visible categories

Fixes #491
This commit is contained in:
Tulir Asokan 2024-12-08 01:02:11 +02:00
parent 152942663f
commit 1b0a8d6192
3 changed files with 36 additions and 3 deletions

View file

@ -90,6 +90,7 @@ export const EmojiGroup = ({
ref={divRef} ref={divRef}
className="emoji-category" className="emoji-category"
id={`emoji-category-${categoryID}`} id={`emoji-category-${categoryID}`}
data-category-id={categoryID}
style={{ containIntrinsicHeight: `${1.5 + Math.ceil(emojis.length / 8) * 2.5}rem` }} style={{ containIntrinsicHeight: `${1.5 + Math.ceil(emojis.length / 8) * 2.5}rem` }}
> >
<h4 className="emoji-category-name"> <h4 className="emoji-category-name">

View file

@ -90,6 +90,11 @@ div.emoji-picker {
border-bottom-left-radius: 0; border-bottom-left-radius: 0;
border-bottom-right-radius: 0; border-bottom-right-radius: 0;
border-bottom: 2px solid transparent; border-bottom: 2px solid transparent;
&.visible {
border-bottom: 2px solid var(--primary-color);
}
&:hover { &:hover {
border-bottom: 2px solid var(--primary-color-dark); border-bottom: 2px solid var(--primary-color-dark);
} }

View file

@ -13,7 +13,7 @@
// //
// You should have received a copy of the GNU Affero General Public License // You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
import React, { CSSProperties, JSX, use, useCallback, useState } from "react" import React, { CSSProperties, JSX, use, useCallback, useEffect, useRef, useState } from "react"
import { getMediaURL } from "@/api/media.ts" import { getMediaURL } from "@/api/media.ts"
import { RoomStateStore, useCustomEmojis } from "@/api/statestore" import { RoomStateStore, useCustomEmojis } from "@/api/statestore"
import { roomStateGUIDToString } from "@/api/types" import { roomStateGUIDToString } from "@/api/types"
@ -64,6 +64,8 @@ const EmojiPicker = ({ style, selected, onSelect, room, allowFreeform, closeOnSe
const client = use(ClientContext)! const client = use(ClientContext)!
const [query, setQuery] = useState("") const [query, setQuery] = useState("")
const [previewEmoji, setPreviewEmoji] = useState<Emoji>() const [previewEmoji, setPreviewEmoji] = useState<Emoji>()
const emojiCategoryBarRef = useRef<HTMLDivElement>(null)
const emojiListRef = useRef<HTMLDivElement>(null)
const watchedEmojiPackKeys = client.store.getEmojiPackKeys().map(roomStateGUIDToString) const watchedEmojiPackKeys = client.store.getEmojiPackKeys().map(roomStateGUIDToString)
const customEmojiPacks = useCustomEmojis(client.store, room) const customEmojiPacks = useCustomEmojis(client.store, room)
const emojis = useFilteredEmojis(query, { const emojis = useFilteredEmojis(query, {
@ -89,8 +91,33 @@ const EmojiPicker = ({ style, selected, onSelect, room, allowFreeform, closeOnSe
const categoryID = evt.currentTarget.getAttribute("data-category-id")! const categoryID = evt.currentTarget.getAttribute("data-category-id")!
document.getElementById(`emoji-category-${categoryID}`)?.scrollIntoView() document.getElementById(`emoji-category-${categoryID}`)?.scrollIntoView()
}, []) }, [])
useEffect(() => {
const cats = emojiCategoryBarRef.current
const lists = emojiListRef.current
if (!cats || !lists) {
return
}
const observer = new IntersectionObserver(entries => {
for (const entry of entries) {
const catID = entry.target.getAttribute("data-category-id")
const cat = catID && cats.querySelector(`.emoji-category-icon[data-category-id="${catID}"]`)
if (!cat) {
continue
}
if (entry.isIntersecting) {
cat.classList.add("visible")
} else {
cat.classList.remove("visible")
}
}
})
for (const cat of lists.getElementsByClassName("emoji-category")) {
observer.observe(cat)
}
return () => observer.disconnect()
}, [])
return <div className="emoji-picker" style={style}> return <div className="emoji-picker" style={style}>
<div className="emoji-category-bar"> <div className="emoji-category-bar" ref={emojiCategoryBarRef}>
<button <button
className="emoji-category-icon" className="emoji-category-icon"
data-category-id={CATEGORY_FREQUENTLY_USED} data-category-id={CATEGORY_FREQUENTLY_USED}
@ -132,7 +159,7 @@ const EmojiPicker = ({ style, selected, onSelect, room, allowFreeform, closeOnSe
</div> </div>
<div className="emoji-list"> <div className="emoji-list">
{/* Chrome is dumb and doesn't allow scrolling without an inner div */} {/* Chrome is dumb and doesn't allow scrolling without an inner div */}
<div className="emoji-list-inner"> <div className="emoji-list-inner" ref={emojiListRef}>
{emojis.map(group => { {emojis.map(group => {
if (!group?.length) { if (!group?.length) {
return null return null