When building a blog using the Typecho program, it is common to use the OwO library to provide emoji functionality for the blog, like this:
The integration method provided by the OwO library is more in line with traditional front-end development patterns, while the current open-source version of the Shiro theme is developed based on the NextJS framework, which is not suitable for direct integration into the theme. Therefore, we need to develop a simplified version of the emoji package ourselves.
Create Emoji Data File#
In OwO, emoji data is obtained through the OwO.json
file. For the Shiro theme, we can create a owo.ts
file in the src/lib
directory to define the emoji data. In this file, we create an owo
object and ensure its content is consistent with the structure in OwO.json
to inherit the emoji data from the previous Typecho theme.
export const owo = {
Emoticons: {
type: "emoticon",
container: [
{
icon: 'OωO',
text: 'OωO',
},
//...
],
},
Emoji: {
type: "emoji",
container: [
{
icon: '😂',
text: 'Face with Tears of Joy',
},
//...
],
},
ImageEmojis: {
type: "image",
container: [
{
icon: "https://raw.githubusercontent.com/url8/CDN-EMOTE/main/2233/chijing.png",
text: "face1",
},
//...
],
},
};
Add Emoji Button#
The Shiro theme uses the Emoji Picker library as the emoji selector.
However, because there is frame dropping when scrolling this selector on my computer, I chose to replace the Emoji Picker with my own emoji selector component.
1. Import Custom Emoji Selector Component:
First, in src\components\modules\comment\CommentBox\UniversalTextArea.tsx
, replace the originally imported EmojiPicker
component with our own emoji selector component Stickers
:
const Stickers = dynamic(() =>
import('../../shared/Stickers').then((mod) => mod.Stickers),
)
2. Replace Existing EmojiPicker Component:
Replace <EmojiPicker onEmojiSelect={handleInsertEmoji} />
in the return of the UniversalTextArea
component with <Stickers handleInsertEmoji={handleInsertEmoji} />
:
return (
<TextArea
bordered={false}
wrapperClassName={className}
ref={taRef}
defaultValue={value}
onKeyDown={handleKeyDown}
onChange={(e) => setter('text', e.target.value)}
placeholder={placeholder}
onCmdEnter={(e) => {
e.preventDefault()
sendComment()
}}
>
<CommentBoxSlotPortal>
<>
{/* {!isMobile && (
<FloatPopover
mobileAsSheet
trigger="click"
TriggerComponent={EmojiButton}
headless
popoverClassNames="pointer-events-auto"
>
<EmojiPicker onEmojiSelect={handleInsertEmoji} />
</FloatPopover>
)} */}
<FloatPopover
mobileAsSheet
trigger="click"
TriggerComponent={EmojiButton}
headless
popoverClassNames="pointer-events-auto shadow-perfect relative z-[2] rounded-xl border border-zinc-400/20 bg-white/80 p-4 outline-none backdrop-blur-lg dark:border-zinc-500/30 dark:bg-neutral-900/80 max-w-lg"
>
<Stickers handleInsertEmoji={handleInsertEmoji} />
</FloatPopover>
</>
</CommentBoxSlotPortal>
</TextArea>
)
3. Modify Emoji Button:
Finally, modify the emoji button in the lower left corner of the text box:
const EmojiButton = () => {
return (
<div className="ml-4 text-base md:text-xs" role="button" tabIndex={0}>
<span>OwO</span>
</div>
)
}
This completes the modification of the comment area text box.
Emoji Selector Component#
Next, we need to write the emoji selector component Stickers
that we just imported. This component will be responsible for rendering the emoji selector and handling user click events.
We enter the src\components\modules\shared
directory and create a new file named Stickers.tsx
.
Inside the component, we use useState
to manage the current emoji category currentCategory
(i.e., Emoticons
, Emoji
, or other random categories), and use useMemo
to cache the emoji categories and the currently selected emoji data.
It is important to note: Considering the varying lengths of emoticons, we need to use a Grid layout to align elements when the current emoji package is Emoticons
:
<div
className={`max-h-80 gap-4 overflow-x-hidden overflow-y-scroll pb-4 md:pb-0 ${
currentCategory === 'Emoticons'
? 'grid grid-cols-4'
: 'flex flex-wrap'
}`}
>
{ ... }
</div>
Otherwise, it will look uneven like this:
The complete code for Stickers.tsx
is as follows:
import { memo, useCallback, useMemo, useState } from 'react'
import type { FC } from 'react'
import { owo } from '~/lib/owo'
type EmojisType = typeof owo
type CategoryKey = keyof EmojisType
export const Stickers: FC<{
handleInsertEmoji: (emoji: string) => void
}> = memo(({ handleInsertEmoji }) => {
const [currentCategory, setCurrentCategory] = useState<CategoryKey>('Emoticons')
const emojis: EmojisType = owo
const handleClickEmoji = useCallback(
(emoji: { icon: string; text: any }) => {
if (emoji.icon.startsWith('http')) {
handleInsertEmoji(``)
} else {
handleInsertEmoji(emoji.icon)
}
},
[handleInsertEmoji],
)
const categories = useMemo(
() => Object.keys(emojis) as CategoryKey[],
[emojis],
)
const currentEmojis = useMemo(
() => emojis[currentCategory]?.container || [],
[emojis, currentCategory],
)
return (
<>
{emojis ? (
<>
<div className="mb-2 flex flex-wrap gap-4 text-xs">
{categories.map((category) => (
<button
key={category}
className={`rounded-md px-2 py-1 ${
currentCategory === category ? 'text-accent' : 'opacity-50'
}`}
onClick={() => setCurrentCategory(category)}
>
{category}
</button>
))}
</div>
<div
className={`max-h-80 gap-4 overflow-x-hidden overflow-y-scroll pb-4 md:pb-0 ${
currentCategory === 'Emoticons'
? 'grid grid-cols-4'
: 'flex flex-wrap'
}`}
>
{currentEmojis.map((item: { icon: string; text: string }) => (
<button
key={item.text}
title={item.text}
onClick={() => handleClickEmoji(item)}
style={{
backgroundImage: item.icon.startsWith('http')
? `url(${item.icon})`
: '',
}}
className={
item.icon.startsWith('http')
? 'h-10 w-10 bg-cover bg-center bg-no-repeat'
: ''
}
>
{!item.icon.startsWith('http') && item.icon}
</button>
))}
</div>
</>
) : (
<div>Loading Emojis...</div>
)}
</>
)
})
Stickers.displayName = 'Stickers'
Finally, just rebuild the project, and you can use the emoji functionality in Shiro 😊.
This article was synchronized by Mix Space to xLog. The original link is https://www.vinking.top/posts/codes/integrating-stickers-into-shiro-theme