feat: Bump Fluent Emoji

This commit is contained in:
canisminor1990 2024-11-29 17:50:21 +08:00
parent 3f0b0d47d0
commit d18f5d198c
35 changed files with 1002 additions and 80 deletions

91
.dumirc.ts Normal file
View File

@ -0,0 +1,91 @@
import { defineConfig } from 'dumi';
import { SiteThemeConfig } from 'dumi-theme-lobehub';
import { description, homepage, name } from './package.json';
const isProduction = process.env.NODE_ENV === 'production';
const isWin = process.platform === 'win32';
const themeConfig: SiteThemeConfig = {
actions: [
{
icon: 'Github',
link: homepage,
openExternal: true,
text: 'Github',
},
{
link: '/components/lobe-hub',
text: 'Get Started',
type: 'primary',
},
],
analytics: {
plausible: {
domain: 'fluent-emoji.lobehub.com',
scriptBaseUrl: 'https://plausible.lobehub-inc.cn',
},
},
apiHeader: {
docUrl: `{github}/tree/master/src/{atomId}/index.md`,
match: ['/components', '/features'],
pkg: name,
sourceUrl: `{github}/tree/master/src/{atomId}/index.tsx`,
},
description,
giscus: {
category: 'Q&A',
categoryId: 'DIC_kwDOL_eons4Cktsi',
repo: 'lobehub/fluent-emoji',
repoId: 'R_kgDOL_eong',
},
metadata: {
openGraph: {
image: 'https://github.com/user-attachments/assets/b2b35675-b6dd-43d2-aea1-8139897dbfec',
},
},
name: 'Fluent Emoji',
prefersColor: {
default: 'dark',
switch: false,
},
socialLinks: {
discord: 'https://discord.gg/AYFPHvv2jT',
github: homepage,
},
title: 'Lobe Icons',
};
export default defineConfig({
apiParser: isProduction ? {} : false,
base: '/',
define: {
'process.env': process.env,
},
exportStatic: {},
extraBabelPlugins: ['babel-plugin-antd-style'],
favicons: ['https://lobehub.com/favicon.ico'],
jsMinifier: 'swc',
locales: [{ id: 'en-US', name: 'English' }],
mfsu: isWin ? undefined : {},
npmClient: 'pnpm',
publicPath: '/',
resolve: isProduction
? {
entryFile: './src/index.ts',
}
: undefined,
sitemap: {
hostname: 'https://fluent-emoji.lobehub.com',
},
ssr: isProduction ? {} : false,
styles: [
`html, body { background: transparent; }
@media (prefers-color-scheme: dark) {
html, body { background: #000; }
}`,
],
themeConfig,
title: 'Fluent Emoji',
});

5
.fatherrc.ts Normal file
View File

@ -0,0 +1,5 @@
import { defineConfig } from 'father';
export default defineConfig({
esm: { output: 'es', ignores: ['./src/components/**/*', './src/Editor/**/*'] },
});

View File

@ -17,6 +17,12 @@ jobs:
- name: Install deps
run: bun i
- name: CI
run: bun run ci
- name: Test
run: bun run test
- name: Build
run: bun run build
@ -25,3 +31,23 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
# - name: Build Static
# run: bun run build:static
#
# - name: Commit changes
# run: |-
# git diff
# git config --global user.name "lobehubbot"
# git config --global user.email "i@lobehub.com"
# git add .
# git commit -m "✨ feat(auto): Auto build static emojis" || exit 0
# git push
# env:
# GH_TOKEN: ${{ secrets.GH_TOKEN }}
#
# - name: Release Static
# run: bun run release:static
# env:
# GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
# NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

27
.github/workflows/test.yml vendored Normal file
View File

@ -0,0 +1,27 @@
name: Test CI
on:
pull_request:
push:
branches:
- '!main'
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install bun
uses: oven-sh/setup-bun@v2
- name: Install deps
run: bun i
- name: CI
run: bun run ci
- name: Test and coverage
run: bun run test:coverage
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4

View File

@ -1,4 +1,3 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npx --no -- commitlint --edit ${1}

View File

@ -1,4 +1,3 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npx --no-install lint-staged

3
CHANGELOG.md Normal file
View File

@ -0,0 +1,3 @@
<a name="readme-top"></a>
# Changelog

168
README.md Normal file
View File

@ -0,0 +1,168 @@
<div align="center"><a name="readme-top"></a>
<img height="120" src="https://registry.npmmirror.com/@lobehub/assets-logo/1.0.0/files/assets/logo-3d.webp">
<img height="120" src="https://gw.alipayobjects.com/zos/kitchen/qJ3l3EPsdW/split.svg">
<img height="120" src="https://registry.npmmirror.com/@lobehub/fluent-emoji-3d/latest/files/assets/1f600.webp">
<h1>Fluent Emoji</h1>
A collection of familiar, friendly, and modern emoji from Microsoft. See them all on one page at [fluent-emoji.lobehub.com](https://fluent-emoji.lobehub.com).<br/>
Contributions, corrections & requests can be made on GitHub.
[![][npm-release-shield]][npm-release-link]
[![][github-releasedate-shield]][github-releasedate-link]
[![][github-action-test-shield]][github-action-test-link]
[![][github-action-release-shield]][github-action-release-link]<br/>
[![][github-contributors-shield]][github-contributors-link]
[![][github-forks-shield]][github-forks-link]
[![][github-stars-shield]][github-stars-link]
[![][github-issues-shield]][github-issues-link]
[![][github-license-shield]][github-license-link]
[Changelog](./CHANGELOG.md) · [Report Bug][github-issues-link] · [Request Feature][github-issues-link]
![](https://github.com/user-attachments/assets/b2b35675-b6dd-43d2-aea1-8139897dbfec)
</div>
## ✨ Features
- 🚀 **Lightweight & Scalable**: Emojis are designed to be lightweight, utilizing highly optimized scalable vector graphics (SVG) / WebP for the best performance and quality.
- 🌳 **Tree Shakable**: The collection is tree-shakable, ensuring that you only import the emojis that you use by cdn, which helps in reducing the overall bundle size of your project.
- 👥 **Active Community**: Fluent Emoji boasts an active community of designers and developers. Engage with us on platforms like GitHub and Discord to contribute or get support.
<div align="right">
[![][back-to-top]](#readme-top)
</div>
## 📦 Installation
> \[!IMPORTANT]\
> This package is [ESM only](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c).
To install `@lobehub/fluent-emoji`, run the following command:
[![][bun-shield]][bun-link]
```bash
$ bun add @lobehub/fluent-emoji
```
### Compile with Next.js
> \[!NOTE]\
> By work correct with Next.js SSR, add `transpilePackages: ['@lobehub/fluent-emoji']` to `next.config.js`. For example:
```js
const nextConfig = {
transpilePackages: ['@lobehub/fluent-emoji'],
};
```
<div align="right">
[![][back-to-top]](#readme-top)
</div>
## ⌨️ Local Development
You can use Github Codespaces for online development:
[![][github-codespace-shield]][github-codespace-link]
Or clone it for local development:
[![][bun-shield]][bun-link]
```bash
$ git clone https://github.com/lobehub/fluent-emoji.git
$ cd fluent-emoji
$ bun install
$ bun dev
```
<div align="right">
[![][back-to-top]](#readme-top)
</div>
## 🤝 Contributing
Contributions of all types are more than welcome, if you are interested in contributing code, feel free to check out our GitHub [Issues][github-issues-link] to get stuck in to show us what youre made of.
[![][pr-welcome-shield]][pr-welcome-link]
[![][github-contrib-shield]][github-contrib-link]
<div align="right">
[![][back-to-top]](#readme-top)
</div>
## 🔗 Links
### More Products
- **[🤯 Lobe Chat](https://github.com/lobehub/lobe-chat)** - An open-source, extensible (Function Calling), high-performance chatbot framework. It supports one-click free deployment of your private ChatGPT/LLM web application.
- **[🅰️ Lobe Theme](https://github.com/lobehub/sd-webui-lobe-theme)** - The modern theme for stable diffusion webui, exquisite interface design, highly customizable UI, and efficiency boosting features.
- **[🧸 Lobe Vidol](https://github.com/lobehub/lobe-vidol)** - Experience the magic of virtual idol creation with Lobe Vidol, enjoy the elegance of our Exquisite UI Design, dance along using MMD Dance Support, and engage in Smooth Conversations.
### Design Resources
- **[🍭 Lobe UI](https://ui.lobehub.com)** - An open-source UI component library for building AIGC web apps.
- **[🥨 Lobe Icons](https://lobehub.com/icons)** - Popular AI / LLM Model Brand SVG Logo and Icon Collection.
- **[📊 Lobe Charts](https://charts.lobehub.com)** - React modern charts components built on recharts
### Development Resources
- **[🎤 Lobe TTS](https://tts.lobehub.com)** - A high-quality & reliable TTS/STT library for Server and Browser
- **[🌏 Lobe i18n](https://github.com/lobehub/lobe-cli-toolbox/blob/master/packages/lobe-i18n)** - Automation ai tool for the i18n (internationalization) translation process.
[More Resources](https://lobehub.com/resources)
<div align="right">
[![][back-to-top]](#readme-top)
</div>
---
#### 📝 License
Copyright © 2024 [LobeHub][profile-link]. <br />
This project is [MIT](./LICENSE) licensed.
[back-to-top]: https://img.shields.io/badge/-BACK_TO_TOP-black?style=flat-square
[bun-link]: https://bun.sh
[bun-shield]: https://img.shields.io/badge/-speedup%20with%20bun-black?logo=bun&style=for-the-badge
[github-action-release-link]: https://github.com/lobehub/fluent-emoji/actions/workflows/release.yml
[github-action-release-shield]: https://img.shields.io/github/actions/workflow/status/lobehub/fluent-emoji/release.yml?label=release&labelColor=black&logo=githubactions&logoColor=white&style=flat-square
[github-action-test-link]: https://github.com/lobehub/fluent-emoji/actions/workflows/test.yml
[github-action-test-shield]: https://img.shields.io/github/actions/workflow/status/lobehub/fluent-emoji/test.yml?label=test&labelColor=black&logo=githubactions&logoColor=white&style=flat-square
[github-codespace-link]: https://codespaces.new/lobehub/fluent-emoji
[github-codespace-shield]: https://github.com/codespaces/badge.svg
[github-contrib-link]: https://github.com/lobehub/fluent-emoji/graphs/contributors
[github-contrib-shield]: https://contrib.rocks/image?repo=lobehub%2Ffluent-emoji
[github-contributors-link]: https://github.com/lobehub/fluent-emoji/graphs/contributors
[github-contributors-shield]: https://img.shields.io/github/contributors/lobehub/fluent-emoji?color=c4f042&labelColor=black&style=flat-square
[github-forks-link]: https://github.com/lobehub/fluent-emoji/network/members
[github-forks-shield]: https://img.shields.io/github/forks/lobehub/fluent-emoji?color=8ae8ff&labelColor=black&style=flat-square
[github-issues-link]: https://github.com/lobehub/fluent-emoji/issues
[github-issues-shield]: https://img.shields.io/github/issues/lobehub/fluent-emoji?color=ff80eb&labelColor=black&style=flat-square
[github-license-link]: https://github.com/lobehub/fluent-emoji/blob/master/LICENSE
[github-license-shield]: https://img.shields.io/github/license/lobehub/fluent-emoji?color=white&labelColor=black&style=flat-square
[github-releasedate-link]: https://github.com/lobehub/fluent-emoji/releases
[github-releasedate-shield]: https://img.shields.io/github/release-date/lobehub/fluent-emoji?labelColor=black&style=flat-square
[github-stars-link]: https://github.com/lobehub/fluent-emoji/network/stargazers
[github-stars-shield]: https://img.shields.io/github/stars/lobehub/fluent-emoji?color=ffcb47&labelColor=black&style=flat-square
[npm-release-link]: https://www.npmjs.com/package/@lobehub/fluent-emoji
[npm-release-shield]: https://img.shields.io/npm/v/@lobehub/fluent-emoji?color=369eff&labelColor=black&logo=npm&logoColor=white&style=flat-square
[pr-welcome-link]: https://github.com/lobehub/fluent-emoji/pulls
[pr-welcome-shield]: https://img.shields.io/badge/%F0%9F%A4%AF%20PR%20WELCOME-%E2%86%92-ffcb47?labelColor=black&style=for-the-badge
[profile-link]: https://github.com/lobehub

9
docs/changelog.md Normal file
View File

@ -0,0 +1,9 @@
---
title: Changelog
description: New updates and improvements to @lobehub/icons
nav:
title: Changelog
order: 999
---
<embed src="../CHANGELOG.md"></embed>

7
docs/index.md Normal file
View File

@ -0,0 +1,7 @@
---
hero:
title: Fluent <b>Emoji</b>
description: Fluent Emoji are a collection of familiar, friendly, and modern emoji from Microsoft
---
<code src="./index.tsx" inline></code>

39
docs/index.tsx Normal file
View File

@ -0,0 +1,39 @@
import { Features, FeaturesProps, Snippet } from '@lobehub/ui';
import { Expand, GitPullRequest, Trees } from 'lucide-react';
import { Center, Flexbox } from 'react-layout-kit';
import Dashboard from '@/components/Dashboard';
const items: FeaturesProps['items'] = [
{
description:
'Emojis are designed to be lightweight, utilizing highly optimized scalable vector graphics (SVG) / WebP for the best performance and quality.',
icon: Expand,
title: 'Lightweight & Scalable',
},
{
description:
'The collection is tree-shakable, ensuring that you only import the emojis that you use by cdn, which helps in reducing the overall bundle size of your project.',
icon: Trees,
title: 'Tree Shakable',
},
{
description:
'Fluent Emoji boasts an active community of designers and developers. Engage with us on platforms like GitHub and Discord to contribute or get support.',
icon: GitPullRequest,
title: 'Active Community',
},
];
export default () => {
return (
<Flexbox gap={48}>
<Center>
<h2 style={{ fontSize: 20 }}>To install Fluent Emoji, run the following command:</h2>
<Snippet language={'bash'}>{'$ bun add @lobehub/fluent-emoji'}</Snippet>
</Center>
<Dashboard />
<Features items={items} />
</Flexbox>
);
};

View File

@ -1,11 +0,0 @@
{
"command": {
"bootstrap": {
"hoist": true
}
},
"npmClient": "pnpm",
"packages": ["packages/*"],
"useWorkspaces": true,
"version": "independent"
}

View File

@ -18,18 +18,36 @@
"license": "MIT",
"author": "LobeHub <i@lobehub.com>",
"sideEffects": false,
"workspaces": [
"packages/*"
"main": "es/index.js",
"module": "es/index.js",
"types": "es/index.d.ts",
"files": [
"es"
],
"scripts": {
"build": "tsx ./src/groupAnim.ts",
"build": "npm run build:toc && father build",
"build:static": "tsx ./scripts/groupAnim.ts",
"ci": "npm run lint && npm run type-check",
"clean": "rm -r es lib dist coverage .dumi/tmp .eslintcache node_modules/.cache",
"dev": "dumi dev",
"docs:build": "dumi build",
"docs:build-analyze": "ANALYZE=1 dumi build",
"docs:dev": "dumi dev",
"doctor": "father doctor",
"lint": "eslint \"{src,packages}/**/*.{js,jsx,ts,tsx}\" --fix",
"lint:md": "remark . --quiet --frail --output",
"prepare": "husky install",
"prepare": "husky && npm run setup",
"prettier": "prettier -c --write --no-error-on-unmatched-pattern \"**/**\"",
"reinstall": "npm run clean && lerna clean && rm -rf node_modules && npm run",
"release": "multi-semantic-release",
"release:local": "multi-semantic-release --no-ci"
"release:static": "multi-semantic-release",
"setup": "dumi setup",
"start": "npm run dev",
"sync:cnpm": "cnpm sync @lobehub/fluent-emoji-mono @lobehub/fluent-emoji-flat @lobehub/fluent-emoji-modern @lobehub/fluent-emoji-anim-1 @lobehub/fluent-emoji-anim-2 @lobehub/fluent-emoji-anim-3 @lobehub/fluent-emoji-anim-4",
"test": "vitest --passWithNoTests",
"test:coverage": "vitest run --coverage --passWithNoTests",
"test:update": "vitest -u",
"type-check": "tsc -p tsconfig-check.json"
},
"lint-staged": {
"*.md": [
@ -50,23 +68,52 @@
"eslint --fix"
]
},
"dependencies": {
"@lobehub/emojilib": "^1.0.0",
"emoji-regex": "^10.4.0",
"url-join": "^5.0.0"
},
"devDependencies": {
"@commitlint/cli": "^18",
"@lobehub/lint": "latest",
"@types/node": "latest",
"commitlint": "^18",
"eslint": "^8",
"glob": "^10.3.16",
"husky": "^8",
"lint-staged": "^15",
"multi-semantic-release": "^3",
"@babel/runtime": "^7.26.0",
"@commitlint/cli": "^18.6.1",
"@lobehub/lint": "^1.24.4",
"@testing-library/react": "^14.3.1",
"@types/lodash-es": "^4.17.12",
"@types/react": "^18.3.12",
"@types/react-dom": "^18.3.1",
"@vitest/coverage-v8": "~1.2.2",
"babel-plugin-antd-style": "^1.0.4",
"commitlint": "^18.6.1",
"dumi": "^2.4.14",
"dumi-theme-lobehub": "^1.10.9",
"eslint": "^8.57.1",
"glob": "^10.4.5",
"husky": "^9.1.7",
"jsdom": "^23.2.0",
"lint-staged": "^15.2.10",
"lodash-es": "^4.17.21",
"multi-semantic-release": "^3.0.2",
"p-map": "^7.0.2",
"remark": "^14",
"remark-cli": "^11",
"semantic-release": "^21",
"sharp": "^0.33.4",
"tsx": "^4.10.5",
"typescript": "^5"
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-virtuoso": "^4.12.0",
"remark": "^14.0.3",
"remark-cli": "^11.0.0",
"semantic-release": "^21.1.2",
"sharp": "^0.33.5",
"stylelint": "^15.11.0",
"tsx": "^4.19.2",
"typescript": "^5.7.2",
"vitest": "~1.2.2"
},
"peerDependencies": {
"@lobehub/ui": ">=1",
"antd": ">=5",
"antd-style": ">=3",
"lucide-react": ">=0.396.0",
"react": ">=18",
"react-dom": ">=18",
"react-layout-kit": ">=1"
},
"publishConfig": {
"access": "public",

View File

@ -1,2 +1,3 @@
packages:
- "*"
- 'packages/**'

5
public/robots.txt Normal file
View File

@ -0,0 +1,5 @@
User-Agent: *
Allow: /
Host: https://fluent-emoji.lobehub.com
Sitemap: https://fluent-emoji.lobehub.com/sitemap.xml

13
renovate.json Normal file
View File

@ -0,0 +1,13 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"automerge": false,
"dependencyDashboard": true,
"ignoreDeps": [],
"labels": ["dependencies"],
"postUpdateOptions": ["yarnDedupeHighest"],
"prConcurrentLimit": 30,
"prHourlyLimit": 0,
"rebaseWhen": "conflicted",
"schedule": "on sunday before 6:00am",
"timezone": "UTC"
}

0
scripts/png2webp.ts Normal file
View File

View File

@ -0,0 +1,45 @@
import {
FluentEmoji,
type FluentEmojiProps,
getEmoji,
getEmojiNameByCharacter,
} from '@lobehub/fluent-emoji';
import { StoryBook, useControls, useCreateStore } from '@lobehub/ui';
import { Button } from 'antd';
import { Flexbox } from 'react-layout-kit';
export default () => {
const store = useCreateStore();
const control: FluentEmojiProps | any = useControls(
{
cdn: {
options: ['aliyun', 'unpkg'],
value: 'aliyun',
},
emoji: '🤯',
size: {
max: 128,
min: 16,
step: 1,
value: 64,
},
},
{ store },
);
return (
<StoryBook levaStore={store}>
<Flexbox align={'center'} gap={8}>
<Flexbox gap={8} horizontal>
<FluentEmoji type={'anim'} {...control} />
<FluentEmoji type={'3d'} {...control} />
<FluentEmoji type={'modern'} {...control} />
<FluentEmoji type={'flat'} {...control} />
<FluentEmoji type={'mono'} {...control} />
<FluentEmoji type={'pure'} {...control} />
</Flexbox>
<Button icon={getEmoji(control.emoji)}>{getEmojiNameByCharacter(control.emoji)}</Button>
</Flexbox>
</StoryBook>
);
};

14
src/FluentEmoji/index.md Normal file
View File

@ -0,0 +1,14 @@
---
nav: Components
group: Components
title: FluentEmoji
description: The FluentEmoji component is used to display an emoji with the Fluent design style. It supports different types of styles (modern, flat, high-contrast) and sizes. It also provides a loading spinner when the image is being fetched from the server, and a fallback text emoji when the image fails to load. The component is customizable with className and style props, and can receive additional props to be passed down to the underlying div element.
---
## Default
<code src="./demos/index.tsx" nopadding></code>
## APIs
<API></API>

82
src/FluentEmoji/index.tsx Normal file
View File

@ -0,0 +1,82 @@
'use client';
import { ElementType, createElement, forwardRef, useMemo, useState } from 'react';
import { type HTMLAttributes } from 'react';
import { Center } from 'react-layout-kit';
import { getFluentEmojiCDN } from '@/getFluentEmojiCDN';
import { EmojiType } from '@/getFluentEmojiCDN/utils';
import { useStyles } from './style';
const createContainer = (as: ElementType) =>
forwardRef((props: any, ref) => createElement(as, { ...props, ref }));
export interface FluentEmojiProps extends HTMLAttributes<HTMLDivElement> {
as?: ElementType;
cdn?: 'aliyun' | 'unpkg';
emoji: string;
size?: number;
type?: EmojiType | 'pure';
unoptimized?: boolean;
}
const FluentEmoji = forwardRef<any, FluentEmojiProps>(
(
{
emoji,
className,
style,
type = '3d',
cdn = 'aliyun',
size = 40,
unoptimized,
as = 'img',
onError,
},
ref,
) => {
const [loadingFail, setLoadingFail] = useState(false);
const { cx, styles } = useStyles();
const ImgContainer = useMemo(() => createContainer(as), [as]);
const emojiUrl = useMemo(() => {
if (type === 'pure') return null;
return getFluentEmojiCDN(emoji, { cdn, type });
}, [type, emoji]);
if (type === 'pure' || !emojiUrl || loadingFail)
return (
<Center
className={cx(styles.container, className)}
flex={'none'}
height={size}
style={{ fontSize: size * 0.9, ...style }}
width={size}
>
{emoji}
</Center>
);
return (
<ImgContainer
alt={emoji}
className={className}
height={size}
loading={'lazy'}
onError={(e: any) => {
setLoadingFail(true);
onError?.(e);
}}
ref={ref}
src={emojiUrl}
style={{ flex: 'none', ...style }}
unoptimized={unoptimized}
width={size}
/>
);
},
);
export default FluentEmoji;

11
src/FluentEmoji/style.ts Normal file
View File

@ -0,0 +1,11 @@
import { createStyles } from 'antd-style';
export const useStyles = createStyles(({ css }) => {
return {
container: css`
position: relative;
line-height: 1;
text-align: center;
`,
};
});

View File

@ -0,0 +1,85 @@
import emojilib from '@lobehub/emojilib';
import { FluentEmoji, FluentEmojiProps, getFluentEmojiCDN } from '@lobehub/fluent-emoji';
import { SearchBar } from '@lobehub/ui';
import { Segmented } from 'antd';
import { useTheme } from 'antd-style';
import { memo, useMemo, useState } from 'react';
import { Flexbox } from 'react-layout-kit';
import EmojiItem from './EmojiItem';
import VirtuosoGridList from './VirtuosoGridList';
const Dashboard = memo(() => {
const theme = useTheme();
const [type, setType] = useState<FluentEmojiProps['type']>('3d');
const [keyword, setKeyword] = useState<string>();
const list = useMemo(() => {
if (!keyword) return Object.entries(emojilib);
return Object.entries(emojilib).filter(([emoji, name]) => {
return emoji.includes(keyword) || name.toLowerCase().includes(keyword.toLowerCase());
});
}, [keyword]);
return (
<Flexbox gap={16} style={{ maxWidth: 960 }} width={'100%'}>
<Flexbox align={'center'} gap={12} horizontal>
<SearchBar
defaultValue={keyword}
onSearch={(v) => setKeyword(v)}
placeholder={'Search by emoji keywords...'}
style={{ width: '100%' }}
type={'block'}
/>
<Segmented
defaultValue={type}
onChange={(v: FluentEmojiProps['type']) => setType(v)}
options={[
{
label: 'Mono',
value: 'mono',
},
{
label: 'Flat',
value: 'flat',
},
{
label: 'Modern',
value: 'modern',
},
{
label: '3D',
value: '3d',
},
{
label: 'Anim',
value: 'anim',
},
]}
style={{
border: `1px solid ${theme.colorBorder}`,
}}
/>
</Flexbox>
<VirtuosoGridList
data={list}
initialItemCount={24}
itemContent={(_, [emoji, name]) => (
<EmojiItem
emoji={emoji}
key={name}
title={name}
url={getFluentEmojiCDN(emoji, { type: type as any })}
>
<FluentEmoji emoji={emoji} key={name} size={56} type={type} />
</EmojiItem>
)}
style={{
minHeight: '1050px',
}}
/>
</Flexbox>
);
});
export default Dashboard;

View File

@ -0,0 +1,76 @@
import { CopyButton } from '@lobehub/ui';
import { Typography } from 'antd';
import { createStyles } from 'antd-style';
import { capitalize } from 'lodash-es';
import { ReactNode, memo, useRef } from 'react';
import { Center, Flexbox } from 'react-layout-kit';
const useStyles = createStyles(({ css, token }) => {
return {
card: css`
position: relative;
overflow: hidden;
background: ${token.colorBgContainer};
border: 1px solid ${token.colorBorderSecondary};
border-radius: ${token.borderRadiusLG}px;
`,
row: css`
border-block-start: 1px solid ${token.colorFillSecondary};
`,
title: css`
margin: 0;
font-size: 12px !important;
`,
titleRow: css`
margin-block-start: -8px;
`,
};
});
interface IconItemProps {
children: ReactNode;
emoji: string;
title: string;
url: string;
}
const EmojiItem = memo<IconItemProps>(({ children, title, url, emoji }) => {
const { styles } = useStyles();
const ref = useRef<HTMLDivElement>(null);
return (
<Flexbox className={styles.card}>
<Center height={96} ref={ref} style={{ position: 'relative' }} width={'100%'}>
{children}
</Center>
<Flexbox
align={'center'}
className={styles.titleRow}
horizontal
justify={'space-between'}
paddingBlock={8}
paddingInline={12}
width={'100%'}
>
<Typography.Title className={styles.title} ellipsis={{ rows: 1 }} level={2}>
{capitalize(title.replaceAll('-', ' '))}
</Typography.Title>
</Flexbox>
<Flexbox align={'center'} className={styles.row} horizontal>
<Flexbox flex={1} horizontal paddingInline={12}>
<Center flex={1} height={32}>
{emoji}
</Center>
<Center flex={1} height={32}>
<CopyButton content={url} size={'small'} />
</Center>
</Flexbox>
</Flexbox>
</Flexbox>
);
});
export default EmojiItem;

View File

@ -0,0 +1,28 @@
import { Grid, GridProps } from '@lobehub/ui';
import { forwardRef, memo } from 'react';
import { VirtuosoGrid, VirtuosoGridProps } from 'react-virtuoso';
const GridList = forwardRef<HTMLDivElement, GridProps>((props, ref) => (
<Grid gap={16} maxItemWidth={160} ref={ref} rows={5} {...props} />
));
const VirtuosoGridList = memo<VirtuosoGridProps<any, any>>(
({ data, initialItemCount, ...rest }) => {
const count = data && data?.length >= 8 ? 8 : data?.length;
const maxInitialItemCount =
data && data?.length && initialItemCount && initialItemCount > data?.length
? data?.length
: initialItemCount;
return (
<VirtuosoGrid
components={{ List: GridList }}
data={data}
initialItemCount={maxInitialItemCount || count}
overscan={400}
{...rest}
/>
);
},
);
export default VirtuosoGridList;

View File

@ -0,0 +1,15 @@
import { useEffect, useState } from 'react';
export const useScrollParent = () => {
const [parent, setParent] = useState<HTMLDivElement>();
useEffect(() => {
if (typeof document === 'undefined') return;
const scrollParent = document.querySelector(`#root`);
if (scrollParent) {
setParent(scrollParent as any);
}
}, []);
return parent;
};

View File

@ -0,0 +1,32 @@
---
nav: Components
group:
title: Utils
order: 999
title: getEmojiByCharacter
atomId: 'getEmojiNameByCharacter, getEmoji'
---
## getEmojiNameByCharacter
```tsx
import { getEmojiNameByCharacter } from '@lobehub/fluent-emoji';
export default () => {
const name = getEmojiNameByCharacter('🤯');
return name;
};
```
## getEmoji
```tsx
import { getEmoji } from '@lobehub/fluent-emoji';
export default () => {
const emoji = getEmoji('🤯 in string');
return emoji;
};
```

View File

@ -0,0 +1,15 @@
import emojilib from '@lobehub/emojilib';
import emojiRegex from 'emoji-regex';
export const getEmoji = (emoji: string): string | undefined => {
const regex = emojiRegex();
const pureEmoji = emoji.match(regex)?.[0];
return pureEmoji;
};
export const getEmojiNameByCharacter = (emoji: string): string | undefined => {
const pureEmoji = getEmoji(emoji);
if (!pureEmoji) return;
const EmojiLab: any = emojilib;
return EmojiLab?.[pureEmoji];
};

View File

@ -0,0 +1,41 @@
---
nav: Components
group:
title: Utils
order: 999
title: getFluentEmojiCDN
---
## Default
```tsx
import { getFluentEmojiCDN } from '@lobehub/fluent-emoji';
import { Grid } from '@lobehub/ui';
export default () => {
const mono = getFluentEmojiCDN('🤯', { type: 'mono' });
const flat = getFluentEmojiCDN('🤯', { type: 'flat' });
const modern = getFluentEmojiCDN('🤯', { type: 'modern' });
const threeD = getFluentEmojiCDN('🤯', { type: '3d' });
const anim = getFluentEmojiCDN('🤯', { type: 'anim' });
return (
<Grid rows={5} maxItemWidth={48}>
<img height={48} src={mono} alt="🤯" />
<img height={48} src={flat} alt="🤯" />
<img height={48} src={modern} alt="🤯" />
<img height={48} src={threeD} alt="🤯" />
<img height={48} src={anim} alt="🤯" />
</Grid>
);
};
```
## Options
```ts
export interface FluentEmojiCdnConfig {
cdn?: 'aliyun' | 'unpkg';
type?: 'anim' | 'flat' | 'modern' | 'mono' | '3d';
}
```

View File

@ -0,0 +1,20 @@
import urlJoin from 'url-join';
import { EmojiType, genEmojiUrl } from './utils';
export interface FluentEmojiCdnConfig {
cdn?: 'aliyun' | 'unpkg';
type: EmojiType;
}
const ALIYUN_ICON_CDN = ({ pkg, path }: { path: string; pkg: string }) =>
urlJoin('https://registry.npmmirror.com', pkg, 'latest/files', path);
const UNPKG_ICON_CDN = ({ pkg, path }: { path: string; pkg: string }) =>
urlJoin('https://unpkg.com', `${pkg}@latest`, path);
export const getFluentEmojiCDN = (id: string, config?: FluentEmojiCdnConfig): string => {
const { type = '3d', cdn = 'aliyun' } = config || {};
const emoji = genEmojiUrl(id, type);
return cdn === 'unpkg' ? UNPKG_ICON_CDN(emoji) : ALIYUN_ICON_CDN(emoji);
};

View File

@ -0,0 +1,60 @@
export type EmojiType = 'anim' | 'flat' | 'modern' | 'mono' | '3d';
export function isFlagEmoji(emoji: string) {
const flagRegex = /(?:\uD83C[\uDDE6-\uDDFF]){2}/;
return flagRegex.test(emoji);
}
export function emojiToUnicode(emoji: string) {
return [...emoji].map((char) => char?.codePointAt(0)?.toString(16)).join('-');
}
export function emojiAnimPkg(emoji: string) {
const mainPart = emojiToUnicode(emoji).split('-')[0];
if (mainPart < '1f469') {
return '@lobehub/fluent-emoji-anim-1';
} else if (mainPart >= '1f469' && mainPart < '1f620') {
return '@lobehub/fluent-emoji-anim-2';
} else if (mainPart >= '1f620' && mainPart < '1f9a0') {
return '@lobehub/fluent-emoji-anim-3';
} else {
return '@lobehub/fluent-emoji-anim-4';
}
}
export const genEmojiUrl = (emoji: string, type: EmojiType) => {
const ext = ['anim', '3d'].includes(type) ? 'webp' : 'svg';
switch (type) {
case 'anim': {
return {
path: `assets/${emojiToUnicode(emoji)}.${ext}`,
pkg: emojiAnimPkg(emoji),
};
}
case '3d': {
return {
path: `assets/${emojiToUnicode(emoji)}.${ext}`,
pkg: '@lobehub/fluent-emoji-3d',
};
}
case 'flat': {
return {
path: `assets/${emojiToUnicode(emoji)}.${ext}`,
pkg: '@lobehub/fluent-emoji-flat',
};
}
case 'modern': {
return {
path: `assets/${emojiToUnicode(emoji)}.${ext}`,
pkg: '@lobehub/fluent-emoji-modern',
};
}
case 'mono': {
return {
path: `assets/${emojiToUnicode(emoji)}.${ext}`,
pkg: '@lobehub/fluent-emoji-mono',
};
}
}
};

3
src/index.ts Normal file
View File

@ -0,0 +1,3 @@
export { default as FluentEmoji, type FluentEmojiProps } from './FluentEmoji';
export { getEmoji, getEmojiNameByCharacter } from './getEmojiByCharacter';
export { type FluentEmojiCdnConfig, getFluentEmojiCDN } from './getFluentEmojiCDN';

View File

@ -1,29 +0,0 @@
import { globSync } from 'glob';
import { resolve } from 'node:path';
import pMap from 'p-map';
import sharp from 'sharp';
const fixWinPath = (path: string) => path.replaceAll('\\', '/');
const root = resolve(__dirname, '../');
export const emoji3D = globSync(fixWinPath(resolve(root, 'assets/3d/*.png')));
const run = async () => {
await pMap(
emoji3D,
async (emoji) => {
const filepath = fixWinPath(emoji);
const filename = filepath.split('/').pop();
const output = resolve(root, `packages/3d/assets/${filename?.replaceAll('.png', '.webp')}`);
sharp(filepath)
.webp()
.toFile(output, (err) => {
if (err) console.error(err);
console.log(output);
});
},
{ concurrency: 10 },
);
};
run();

View File

@ -1,25 +1,21 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"compilerOptions": {
"target": "ESNext",
"baseUrl": ".",
"declaration": true,
"downlevelIteration": true,
"esModuleInterop": true,
"jsx": "react-jsx",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"paths": {
"@@/*": [".dumi/tmp/*"],
"@/*": ["src/*"],
"@lobehub/fluent-emoji": ["src"],
"@lobehub/fluent-emoji/*": ["src/*"]
},
"resolveJsonModule": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
"types": ["vitest/globals"]
},
"exclude": ["node_modules"],
"include": ["src", "**/*.ts", "**/*.d.ts", "**/*.tsx", ".next/types/**/*.ts"]
"include": ["src", "docs", "scripts", ".dumirc.ts", "*.ts"]
}