08021948
|
@ -0,0 +1,10 @@
|
||||||
|
DATABASE_URL="postgresql://root:Letusdoit000@localhost:5432/mydb?schema=public"
|
||||||
|
REDIS_HOST=localhost
|
||||||
|
REDIS_PORT=6379
|
||||||
|
# .env
|
||||||
|
ELASTICSEARCH_NODE=http://localhost:9200
|
||||||
|
ELASTICSEARCH_USERNAME=elastic
|
||||||
|
ELASTICSEARCH_PASSWORD=RXoH-DI1lgJB-hY0*pLk
|
||||||
|
TUS_URL=http://localhost:8080
|
||||||
|
PYTHON_URL=http://localhost:8000
|
||||||
|
APP_URL=http://localhost:5173
|
|
@ -20,15 +20,23 @@
|
||||||
"test:e2e": "jest --config ./test/jest-e2e.json"
|
"test:e2e": "jest --config ./test/jest-e2e.json"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@nestjs/bullmq": "^10.2.0",
|
||||||
"@nestjs/common": "^10.3.10",
|
"@nestjs/common": "^10.3.10",
|
||||||
|
"@nestjs/config": "^3.2.3",
|
||||||
"@nestjs/core": "^10.0.0",
|
"@nestjs/core": "^10.0.0",
|
||||||
"@nestjs/platform-express": "^10.0.0",
|
"@nestjs/platform-express": "^10.0.0",
|
||||||
|
"@nestjs/websockets": "^10.3.10",
|
||||||
"@nicestack/common": "workspace:^",
|
"@nicestack/common": "workspace:^",
|
||||||
"@trpc/server": "11.0.0-rc.456",
|
"@trpc/server": "11.0.0-rc.456",
|
||||||
|
"axios": "^1.7.3",
|
||||||
|
"bullmq": "^5.12.0",
|
||||||
"ioredis": "^5.4.1",
|
"ioredis": "^5.4.1",
|
||||||
|
"mime-types": "^2.1.35",
|
||||||
"reflect-metadata": "^0.2.0",
|
"reflect-metadata": "^0.2.0",
|
||||||
"rxjs": "^7.8.1",
|
"rxjs": "^7.8.1",
|
||||||
|
"socket.io": "^4.7.5",
|
||||||
"superjson-cjs": "^2.2.3",
|
"superjson-cjs": "^2.2.3",
|
||||||
|
"tus-js-client": "^4.1.0",
|
||||||
"zod": "^3.23.8"
|
"zod": "^3.23.8"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -37,6 +45,7 @@
|
||||||
"@nestjs/testing": "^10.0.0",
|
"@nestjs/testing": "^10.0.0",
|
||||||
"@types/express": "^4.17.17",
|
"@types/express": "^4.17.17",
|
||||||
"@types/jest": "^29.5.2",
|
"@types/jest": "^29.5.2",
|
||||||
|
"@types/mime-types": "^2.1.4",
|
||||||
"@types/node": "^20.3.1",
|
"@types/node": "^20.3.1",
|
||||||
"@types/supertest": "^6.0.0",
|
"@types/supertest": "^6.0.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
||||||
|
|
|
@ -5,10 +5,12 @@ import { TrpcModule } from './trpc/trpc.module';
|
||||||
import { RedisService } from './redis/redis.service';
|
import { RedisService } from './redis/redis.service';
|
||||||
|
|
||||||
import { RedisModule } from './redis/redis.module';
|
import { RedisModule } from './redis/redis.module';
|
||||||
|
import { SocketGateway } from './socket/socket.gateway';
|
||||||
|
import { QueueModule } from './queue/queue.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [TrpcModule, RedisModule],
|
imports: [TrpcModule, RedisModule, QueueModule],
|
||||||
controllers: [AppController],
|
controllers: [AppController],
|
||||||
providers: [AppService, RedisService],
|
providers: [AppService, RedisService, SocketGateway],
|
||||||
})
|
})
|
||||||
export class AppModule { }
|
export class AppModule { }
|
||||||
|
|
|
@ -4,7 +4,9 @@ import { TrpcRouter } from './trpc/trpc.router';
|
||||||
|
|
||||||
async function bootstrap() {
|
async function bootstrap() {
|
||||||
const app = await NestFactory.create(AppModule);
|
const app = await NestFactory.create(AppModule);
|
||||||
app.enableCors();
|
app.enableCors({
|
||||||
|
origin: [process.env.APP_URL!],
|
||||||
|
});
|
||||||
const trpc = app.get(TrpcRouter);
|
const trpc = app.get(TrpcRouter);
|
||||||
trpc.applyMiddleware(app);
|
trpc.applyMiddleware(app);
|
||||||
await app.listen(3000);
|
await app.listen(3000);
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
import {
|
||||||
|
QueueEventsListener,
|
||||||
|
QueueEventsHost,
|
||||||
|
OnQueueEvent,
|
||||||
|
InjectQueue,
|
||||||
|
} from '@nestjs/bullmq';
|
||||||
|
import { SocketGateway } from '@server/socket/socket.gateway';
|
||||||
|
import { Queue } from 'bullmq';
|
||||||
|
|
||||||
|
|
||||||
|
@QueueEventsListener('general')
|
||||||
|
export class GeneralQueueEvents extends QueueEventsHost {
|
||||||
|
constructor(@InjectQueue('general') private generalQueue: Queue, private socketGateway: SocketGateway) {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnQueueEvent('completed')
|
||||||
|
async onCompleted({
|
||||||
|
jobId,
|
||||||
|
returnvalue
|
||||||
|
}: {
|
||||||
|
jobId: string;
|
||||||
|
returnvalue: string;
|
||||||
|
prev?: string;
|
||||||
|
}) {
|
||||||
|
|
||||||
|
}
|
||||||
|
@OnQueueEvent("progress")
|
||||||
|
async onProgress({ jobId, data }: { jobId: string, data: any }) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
import { InjectQueue } from '@nestjs/bullmq';
|
||||||
|
import { Injectable, Logger, OnModuleInit } from '@nestjs/common';
|
||||||
|
import { Job, Queue } from 'bullmq';
|
||||||
|
import { SocketGateway } from '@server/socket/socket.gateway';
|
||||||
|
@Injectable()
|
||||||
|
export class GeneralQueueService implements OnModuleInit {
|
||||||
|
private readonly logger = new Logger(GeneralQueueService.name,)
|
||||||
|
constructor(@InjectQueue('general') private generalQueue: Queue, private socketGateway: SocketGateway) { }
|
||||||
|
onModuleInit() {
|
||||||
|
this.logger.log(`general queue service init at pid=${process.pid}`)
|
||||||
|
|
||||||
|
}
|
||||||
|
async addJob(data: any) {
|
||||||
|
this.logger.log('add embedding job', data.title)
|
||||||
|
|
||||||
|
await this.generalQueue.add('embedding', data, { debounce: { id: data.id } });
|
||||||
|
|
||||||
|
}
|
||||||
|
async getWaitingJobs() {
|
||||||
|
const waitingJobs = await this.generalQueue.getJobs(["waiting"])
|
||||||
|
return waitingJobs
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
import axios, { AxiosInstance } from 'axios';
|
||||||
|
import { Injectable, Logger } from '@nestjs/common';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class GeneralService {
|
||||||
|
private axiosInstance: AxiosInstance;
|
||||||
|
private logger: Logger;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
const PYTHON_ENDPOINT = process.env.PYTHON_URL || 'http://localhost:8000';
|
||||||
|
this.logger = new Logger(GeneralService.name);
|
||||||
|
this.axiosInstance = axios.create({
|
||||||
|
baseURL: PYTHON_ENDPOINT,
|
||||||
|
timeout: 120000, // 设置请求超时时间
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
import { BullModule } from '@nestjs/bullmq';
|
||||||
|
import { Logger, Module } from '@nestjs/common';
|
||||||
|
|
||||||
|
import { join } from 'path';
|
||||||
|
import { SocketGateway } from '@server/socket/socket.gateway';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [
|
||||||
|
BullModule.forRoot({
|
||||||
|
connection: {
|
||||||
|
host: 'localhost',
|
||||||
|
port: 6379,
|
||||||
|
},
|
||||||
|
}), BullModule.registerQueue({
|
||||||
|
name: 'general',
|
||||||
|
processors: [join(__dirname, 'worker/processor.js')],
|
||||||
|
})
|
||||||
|
],
|
||||||
|
providers: [Logger, SocketGateway],
|
||||||
|
exports: []
|
||||||
|
})
|
||||||
|
export class QueueModule { }
|
|
@ -0,0 +1,5 @@
|
||||||
|
import { Job } from 'bullmq';
|
||||||
|
export default async function (job: Job<any, any, any>) {
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -1,9 +1,10 @@
|
||||||
// redis.module.ts
|
// redis.module.ts
|
||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { RedisService } from './redis.service';
|
import { RedisService } from './redis.service';
|
||||||
|
import { ConfigService } from '@nestjs/config';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
providers: [RedisService], // 注册 RedisService 作为提供者
|
providers: [RedisService, ConfigService], // 注册 RedisService 作为提供者
|
||||||
exports: [RedisService], // 导出 RedisService
|
exports: [RedisService], // 导出 RedisService
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,23 +1,73 @@
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { ConfigService } from '@nestjs/config';
|
||||||
import Redis from 'ioredis';
|
import Redis from 'ioredis';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class RedisService {
|
export class RedisService {
|
||||||
private readonly redisClient: Redis;
|
private readonly redisClient: Redis;
|
||||||
|
|
||||||
constructor() {
|
constructor(private readonly configService: ConfigService) {
|
||||||
this.redisClient = new Redis({
|
this.redisClient = new Redis({
|
||||||
host: process.env.REDIS_HOST,
|
host: configService.get<string>('REDIS_HOST'),
|
||||||
port: parseInt(process.env.REDIS_PORT!), // Redis 服务器的端口
|
port: configService.get<number>('REDIS_PORT'), // Redis 服务器的端口
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
setValue(key: string, value: string) {
|
setValue(key: string, value: string) {
|
||||||
return this.redisClient.set(key, value);
|
return this.redisClient.set(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
getValue(key: string) {
|
getValue(key: string) {
|
||||||
return this.redisClient.get(key);
|
return this.redisClient.get(key);
|
||||||
|
|
||||||
|
}
|
||||||
|
keys(pattern: string) {
|
||||||
|
return this.redisClient.keys(pattern)
|
||||||
|
}
|
||||||
|
setWithExpiry(key: string, value: string, time: number) {
|
||||||
|
return this.redisClient.setex(key, time, value);
|
||||||
|
}
|
||||||
|
deleteKey(key: string) {
|
||||||
|
return this.redisClient.del(key);
|
||||||
|
}
|
||||||
|
setHashField(key: string, field: string, value: string) {
|
||||||
|
return this.redisClient.hset(key, field, value);
|
||||||
|
}
|
||||||
|
//获取key中的field字段数据
|
||||||
|
getHashField(key: string, field: string) {
|
||||||
|
return this.redisClient.hget(key, field);
|
||||||
|
}
|
||||||
|
//获取key中所有数据
|
||||||
|
getAllHashFields(key: string) {
|
||||||
|
return this.redisClient.hgetall(key);
|
||||||
|
}
|
||||||
|
publishMessage(channel: string, message: string) {
|
||||||
|
return this.redisClient.publish(channel, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 订阅消息,需要提供一个回调函数来处理接收到的消息
|
||||||
|
subscribeToMessages(channel: string, messageHandler: (channel: string, message: string) => void) {
|
||||||
|
this.redisClient.subscribe(channel, (err, count) => {
|
||||||
|
if (err) {
|
||||||
|
console.error('Subscription error', err);
|
||||||
|
} else {
|
||||||
|
console.log(`Subscribed to ${count} channels`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.redisClient.on('message', (channel, message) => {
|
||||||
|
console.log(`Received message ${message} from channel ${channel}`);
|
||||||
|
messageHandler(channel, message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 取消订阅指定的频道
|
||||||
|
unsubscribeFromChannel(channel: string) {
|
||||||
|
return this.redisClient.unsubscribe(channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 取消订阅所有频道
|
||||||
|
unsubscribeAll() {
|
||||||
|
return this.redisClient.quit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
import { SocketGateway } from './socket.gateway';
|
||||||
|
|
||||||
|
describe('SocketGateway', () => {
|
||||||
|
let gateway: SocketGateway;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
|
providers: [SocketGateway],
|
||||||
|
}).compile();
|
||||||
|
|
||||||
|
gateway = module.get<SocketGateway>(SocketGateway);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be defined', () => {
|
||||||
|
expect(gateway).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,28 @@
|
||||||
|
import { WebSocketGateway, WebSocketServer, OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect } from '@nestjs/websockets';
|
||||||
|
import { Server, Socket } from 'socket.io';
|
||||||
|
|
||||||
|
@WebSocketGateway(3001, {
|
||||||
|
namespace: 'library-events',
|
||||||
|
cors: {
|
||||||
|
origin: '*', // 或者你可以指定特定的来源,例如 "http://localhost:3000"
|
||||||
|
methods: ['GET', 'POST'],
|
||||||
|
credentials: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
export class SocketGateway implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect {
|
||||||
|
|
||||||
|
@WebSocketServer() server: Server;
|
||||||
|
|
||||||
|
afterInit(server: Server) {
|
||||||
|
console.log('WebSocket initialized');
|
||||||
|
}
|
||||||
|
|
||||||
|
handleConnection(client: Socket, ...args: any[]) {
|
||||||
|
console.log(`Client connected: ${client.id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleDisconnect(client: Socket) {
|
||||||
|
console.log(`Client disconnected: ${client.id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
import * as tus from 'tus-js-client';
|
||||||
|
import { promises as fs } from 'fs';
|
||||||
|
import * as mime from 'mime-types';
|
||||||
|
|
||||||
|
export const uploader = async (
|
||||||
|
endpoint: string = 'http://localhost:8080',
|
||||||
|
input: Buffer | string,
|
||||||
|
externalFileName: string = 'unknown', // 允许外部传入文件名
|
||||||
|
onProgress?: (percentage: number) => void,
|
||||||
|
onSuccess?: (url: string) => void,
|
||||||
|
onError?: (error: Error) => void
|
||||||
|
) => {
|
||||||
|
let fileBuffer: Buffer;
|
||||||
|
let fileName: string;
|
||||||
|
let fileType: string;
|
||||||
|
|
||||||
|
// 确定输入是Buffer还是文件路径
|
||||||
|
if (typeof input === 'string') {
|
||||||
|
try {
|
||||||
|
fileBuffer = await fs.readFile(input);
|
||||||
|
fileName = input.split('/').pop() || 'unknown';
|
||||||
|
fileType = mime.lookup(input) || 'application/octet-stream';
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error("读取文件失败: " + error.message);
|
||||||
|
if (onError) onError(error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fileBuffer = input;
|
||||||
|
fileName = externalFileName; // 使用外部传入的文件名
|
||||||
|
// 尝试获取文件类型,这里简化处理,实际应用中可能需要更复杂的逻辑
|
||||||
|
fileType = mime.lookup(fileName) || 'application/octet-stream';
|
||||||
|
}
|
||||||
|
|
||||||
|
const upload = new tus.Upload(fileBuffer as any, {
|
||||||
|
endpoint: `${endpoint}/files/`,
|
||||||
|
retryDelays: [0, 3000, 5000, 10000, 20000],
|
||||||
|
metadata: { filename: fileName, filetype: fileType },
|
||||||
|
onError: (error) => {
|
||||||
|
console.error("上传失败: " + error.message);
|
||||||
|
if (onError) onError(error);
|
||||||
|
},
|
||||||
|
onProgress: (bytesUploaded, bytesTotal) => {
|
||||||
|
const percentage = (bytesUploaded / bytesTotal) * 100;
|
||||||
|
if (onProgress) onProgress(percentage);
|
||||||
|
},
|
||||||
|
onSuccess: () => {
|
||||||
|
console.log("上传完成");
|
||||||
|
if (onSuccess) onSuccess(upload.url!);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// 寻找并继续之前的上传
|
||||||
|
upload.findPreviousUploads().then((previousUploads) => {
|
||||||
|
if (previousUploads && previousUploads.length > 0) {
|
||||||
|
upload.resumeFromPreviousUpload(previousUploads[0]!);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return upload;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const uploaderPromise = (
|
||||||
|
endpoint: string,
|
||||||
|
input: Buffer | string,
|
||||||
|
externalFileName: string = 'unknown', // 允许外部传入文件名
|
||||||
|
onProgress?: (percentage: number) => void
|
||||||
|
): Promise<string> => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
uploader(endpoint, input, externalFileName, onProgress, resolve, reject)
|
||||||
|
.then((upload) => {
|
||||||
|
upload!.start();
|
||||||
|
})
|
||||||
|
.catch(reject);
|
||||||
|
});
|
||||||
|
};
|
|
@ -0,0 +1,18 @@
|
||||||
|
module.exports = {
|
||||||
|
root: true,
|
||||||
|
env: { browser: true, es2020: true },
|
||||||
|
extends: [
|
||||||
|
'eslint:recommended',
|
||||||
|
'plugin:@typescript-eslint/recommended',
|
||||||
|
'plugin:react-hooks/recommended',
|
||||||
|
],
|
||||||
|
ignorePatterns: ['dist', '.eslintrc.cjs'],
|
||||||
|
parser: '@typescript-eslint/parser',
|
||||||
|
plugins: ['react-refresh'],
|
||||||
|
rules: {
|
||||||
|
'react-refresh/only-export-components': [
|
||||||
|
'warn',
|
||||||
|
{ allowConstantExport: true },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
# React + TypeScript + Vite
|
||||||
|
|
||||||
|
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
|
||||||
|
|
||||||
|
Currently, two official plugins are available:
|
||||||
|
|
||||||
|
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
|
||||||
|
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
|
||||||
|
|
||||||
|
## Expanding the ESLint configuration
|
||||||
|
|
||||||
|
If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:
|
||||||
|
|
||||||
|
- Configure the top-level `parserOptions` property like this:
|
||||||
|
|
||||||
|
```js
|
||||||
|
export default {
|
||||||
|
// other rules...
|
||||||
|
parserOptions: {
|
||||||
|
ecmaVersion: 'latest',
|
||||||
|
sourceType: 'module',
|
||||||
|
project: ['./tsconfig.json', './tsconfig.node.json', './tsconfig.app.json'],
|
||||||
|
tsconfigRootDir: __dirname,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked`
|
||||||
|
- Optionally add `plugin:@typescript-eslint/stylistic-type-checked`
|
||||||
|
- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list
|
|
@ -0,0 +1,33 @@
|
||||||
|
{
|
||||||
|
"name": "@nicestack/iconer",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"main": "./dist/index.umd.js",
|
||||||
|
"module": "./dist/index.es.js",
|
||||||
|
"types": "./types/src/index.d.ts",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "pnpm build && concurrently \"chokidar ./src -c 'pnpm build' -i '**/generated/**'\"",
|
||||||
|
"build": "tsc -b && vite build",
|
||||||
|
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
|
"dependencies": {},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/react": "^18.3.3",
|
||||||
|
"@types/react-dom": "^18.3.0",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^7.15.0",
|
||||||
|
"@typescript-eslint/parser": "^7.15.0",
|
||||||
|
"@vitejs/plugin-react": "^4.3.1",
|
||||||
|
"eslint": "^8.57.0",
|
||||||
|
"eslint-plugin-react-hooks": "^4.6.2",
|
||||||
|
"eslint-plugin-react-refresh": "^0.4.7",
|
||||||
|
"typescript": "^5.2.2",
|
||||||
|
"vite": "^5.3.4",
|
||||||
|
"vite-plugin-svgr": "^4.2.0",
|
||||||
|
"concurrently": "^8.0.1",
|
||||||
|
"chokidar-cli": "^3.0.0",
|
||||||
|
"react": "^18.3.1",
|
||||||
|
"react-dom": "^18.3.1"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
After Width: | Height: | Size: 1.5 KiB |
|
@ -0,0 +1,40 @@
|
||||||
|
import { IconName } from "../generated/icon-names";
|
||||||
|
import { useLazySvgImport } from "../utils/useLazySvgImport";
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
name: IconName;
|
||||||
|
className?: string,
|
||||||
|
svgProp?: React.SVGProps<SVGSVGElement>;
|
||||||
|
size?: 'small' | 'middle' | 'large' | number;
|
||||||
|
}
|
||||||
|
const sizeMap: { [key: string]: number } = {
|
||||||
|
small: 16,
|
||||||
|
middle: 24,
|
||||||
|
large: 32
|
||||||
|
};
|
||||||
|
function Icon(props: IProps) {
|
||||||
|
const { name, svgProp, className = '', size = 'middle' } = props;
|
||||||
|
const { loading, Svg } = useLazySvgImport(name);
|
||||||
|
const finalSize = typeof size === 'number' ? size : sizeMap[size] || sizeMap.middle;
|
||||||
|
const svgStyle = {
|
||||||
|
width: finalSize,
|
||||||
|
height: finalSize,
|
||||||
|
...(svgProp?.style) // 如果svgProp中包含style,则合并样式
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{loading && (
|
||||||
|
<div className={` ${className}`} style={{
|
||||||
|
borderRadius: "100%",
|
||||||
|
height: 24,
|
||||||
|
width: 24
|
||||||
|
}}></div>
|
||||||
|
)}
|
||||||
|
{Svg && (
|
||||||
|
<span className={className}> <Svg style={svgStyle} {...svgProp} /></span>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Icon;
|
|
@ -0,0 +1,2 @@
|
||||||
|
export type IconName = 'align-center' | 'align-justify' | 'align-left' | 'align-right' | 'arrow-drop-down' | 'bold' | 'check' | 'content' | 'copy' | 'edit' | 'get-text' | 'home' | 'horizontal-rule' | 'image' | 'italic' | 'link-off' | 'link' | 'logout' | 'react' | 'redo' | 'share' | 'strike' | 'text-indent' | 'text-outdent' | 'underline' | 'undo' | 'zoomin' | 'zoomout'
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
|
||||||
|
<path fill="currentColor"
|
||||||
|
d="M4.5 20q-.213 0-.356-.144T4 19.499t.144-.356T4.5 19h15q.213 0 .356.144t.144.357t-.144.356T19.5 20zm4-3.75q-.213 0-.356-.144T8 15.749t.144-.356t.356-.143h7q.213 0 .356.144t.144.357t-.144.356t-.356.143zm-4-3.75q-.213 0-.356-.144T4 11.999t.144-.356t.356-.143h15q.213 0 .356.144t.144.357t-.144.356t-.356.143zm4-3.75q-.213 0-.356-.144T8 8.249t.144-.356t.356-.143h7q.213 0 .356.144t.144.357t-.144.356t-.356.143zM4.5 5q-.213 0-.356-.144T4 4.499t.144-.356T4.5 4h15q.213 0 .356.144t.144.357t-.144.356T19.5 5z" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 637 B |
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><path fill="currentColor" d="M4.5 20q-.213 0-.356-.144T4 19.499t.144-.356T4.5 19h15q.213 0 .356.144t.144.357t-.144.356T19.5 20zm0-3.75q-.213 0-.356-.144T4 15.749t.144-.356t.356-.143h15q.213 0 .356.144t.144.357t-.144.356t-.356.143zm0-3.75q-.213 0-.356-.144T4 11.999t.144-.356t.356-.143h15q.213 0 .356.144t.144.357t-.144.356t-.356.143zm0-3.75q-.213 0-.356-.144T4 8.249t.144-.356t.356-.143h15q.213 0 .356.144t.144.357t-.144.356t-.356.143zM4.5 5q-.213 0-.356-.144T4 4.499t.144-.356T4.5 4h15q.213 0 .356.144t.144.357t-.144.356T19.5 5z"/></svg>
|
After Width: | Height: | Size: 623 B |
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><path fill="currentColor" d="M4.5 20q-.213 0-.356-.144T4 19.499t.144-.356T4.5 19h15q.213 0 .356.144t.144.357t-.144.356T19.5 20zm0-3.75q-.213 0-.356-.144T4 15.749t.144-.356t.356-.143h9q.213 0 .356.144t.144.357t-.144.356t-.356.143zm0-3.75q-.213 0-.356-.144T4 11.999t.144-.356t.356-.143h15q.213 0 .356.144t.144.357t-.144.356t-.356.143zm0-3.75q-.213 0-.356-.144T4 8.249t.144-.356t.356-.143h9q.213 0 .356.144t.144.357t-.144.356t-.356.143zM4.5 5q-.213 0-.356-.144T4 4.499t.144-.356T4.5 4h15q.213 0 .356.144t.144.357t-.144.356T19.5 5z"/></svg>
|
After Width: | Height: | Size: 621 B |
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><path fill="currentColor" d="M4.5 5q-.213 0-.356-.144T4 4.499t.144-.356T4.5 4h15q.213 0 .356.144t.144.357t-.144.356T19.5 5zm6 3.75q-.213 0-.356-.144T10 8.249t.144-.356t.356-.143h9q.213 0 .356.144t.144.357t-.144.356t-.356.143zm-6 3.75q-.213 0-.356-.144T4 11.999t.144-.356t.356-.143h15q.213 0 .356.144t.144.357t-.144.356t-.356.143zm6 3.75q-.213 0-.356-.144T10 15.749t.144-.356t.356-.143h9q.213 0 .356.144t.144.357t-.144.356t-.356.143zM4.5 20q-.213 0-.356-.144T4 19.499t.144-.356T4.5 19h15q.213 0 .356.144t.144.357t-.144.356T19.5 20z"/></svg>
|
After Width: | Height: | Size: 624 B |
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><path fill="currentColor" d="m11.565 13.873l-2.677-2.677q-.055-.055-.093-.129q-.037-.073-.037-.157q0-.168.11-.289q.112-.121.294-.121h5.677q.181 0 .292.124t.111.288q0 .042-.13.284l-2.677 2.677q-.093.093-.2.143t-.235.05t-.235-.05t-.2-.143"/></svg>
|
After Width: | Height: | Size: 330 B |
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><path fill="currentColor" d="M8.916 18.25q-.441 0-.74-.299t-.299-.74V6.79q0-.441.299-.74t.74-.299h3.159q1.433 0 2.529.904T15.7 9.006q0 .967-.508 1.693t-1.257 1.065q.913.255 1.55 1.073t.638 1.97q0 1.61-1.202 2.527q-1.202.916-2.646.916zm.236-1.184h3.062q1.161 0 1.875-.7q.715-.699.715-1.627q0-.93-.714-1.629q-.715-.698-1.894-.698H9.152zm0-5.816h2.864q.997 0 1.69-.617t.692-1.546q0-.947-.704-1.553q-.704-.605-1.667-.605H9.152z"/></svg>
|
After Width: | Height: | Size: 517 B |
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><path fill="currentColor" d="m9.55 17.308l-4.97-4.97l.714-.713l4.256 4.256l9.156-9.156l.713.714z"/></svg>
|
After Width: | Height: | Size: 190 B |
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><path fill="currentColor" d="M5.73 15.885h12.54v-1H5.73zm0-3.385h12.54v-1H5.73zm0-3.384h8.77v-1H5.73zM4.616 19q-.69 0-1.153-.462T3 17.384V6.616q0-.691.463-1.153T4.615 5h14.77q.69 0 1.152.463T21 6.616v10.769q0 .69-.463 1.153T19.385 19zm0-1h14.77q.23 0 .423-.192t.192-.424V6.616q0-.231-.192-.424T19.385 6H4.615q-.23 0-.423.192T4 6.616v10.769q0 .23.192.423t.423.192M4 18V6z"/></svg>
|
After Width: | Height: | Size: 464 B |
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><path fill="currentColor" d="M9.116 17q-.691 0-1.153-.462T7.5 15.385V4.615q0-.69.463-1.153T9.116 3h7.769q.69 0 1.153.462t.462 1.153v10.77q0 .69-.462 1.152T16.884 17zm0-1h7.769q.23 0 .423-.192t.192-.423V4.615q0-.23-.192-.423T16.884 4H9.116q-.231 0-.424.192t-.192.423v10.77q0 .23.192.423t.423.192m-3 4q-.69 0-1.153-.462T4.5 18.385V6.615h1v11.77q0 .23.192.423t.423.192h8.77v1zM8.5 16V4z"/></svg>
|
After Width: | Height: | Size: 477 B |
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><path fill="currentColor" d="M5.616 20q-.691 0-1.153-.462T4 18.384V5.616q0-.691.463-1.153T5.616 4h7.178q.25 0 .375.159q.125.158.125.341t-.128.341T12.79 5H5.616q-.231 0-.424.192T5 5.616v12.769q0 .23.192.423t.423.192h12.77q.23 0 .423-.192t.192-.423V11.11q0-.25.159-.375q.158-.125.341-.125t.341.125t.159.375v7.275q0 .69-.462 1.153T18.384 20zM10 13.192v-1.136q0-.323.13-.628q.132-.305.349-.522l8.465-8.465q.166-.166.348-.23t.385-.063q.189 0 .368.064t.326.21L21.483 3.5q.16.166.242.365t.083.4t-.061.382q-.06.18-.226.345l-8.523 8.524q-.217.217-.522.35q-.305.134-.628.134h-1.04q-.349 0-.578-.23t-.23-.578m10.814-8.907l-1.112-1.17zM11 13h1.092l6.666-6.666l-.546-.546l-.61-.584L11 11.806zm7.212-7.211l-.61-.585zl.546.546z"/></svg>
|
After Width: | Height: | Size: 806 B |
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><path fill="currentColor" d="M11.5 16V9h-3V8h7v1h-3v7zm-9.115 5.616v-4.232H4V6.616H2.385V2.385h4.23V4h10.77V2.385h4.23v4.23H20v10.77h1.616v4.23h-4.232V20H6.616v1.616zM6.615 19h10.77v-1.616H19V6.616h-1.616V5H6.616v1.616H5v10.769h1.616z"/></svg>
|
After Width: | Height: | Size: 328 B |
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><path fill="currentColor" d="M5 20V9.5l7-5.288L19 9.5V20h-5.192v-6.384h-3.616V20z"/></svg>
|
After Width: | Height: | Size: 175 B |
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><path fill="currentColor" d="M5.5 12.5q-.213 0-.356-.144T5 11.999t.144-.356t.356-.143h13q.213 0 .356.144t.144.357t-.144.356t-.356.143z"/></svg>
|
After Width: | Height: | Size: 228 B |
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 50 50"><path fill="currentColor" d="M39 38H11c-1.7 0-3-1.3-3-3V15c0-1.7 1.3-3 3-3h28c1.7 0 3 1.3 3 3v20c0 1.7-1.3 3-3 3M11 14c-.6 0-1 .4-1 1v20c0 .6.4 1 1 1h28c.6 0 1-.4 1-1V15c0-.6-.4-1-1-1z"/><path fill="currentColor" d="M30 24c-2.2 0-4-1.8-4-4s1.8-4 4-4s4 1.8 4 4s-1.8 4-4 4m0-6c-1.1 0-2 .9-2 2s.9 2 2 2s2-.9 2-2s-.9-2-2-2m5.3 19.7L19 22.4L9.7 31l-1.4-1.4l10.7-10l17.7 16.7z"/><path fill="currentColor" d="M40.4 32.7L35 28.3L30.5 32l-1.3-1.6l5.8-4.7l6.6 5.4z"/></svg>
|
After Width: | Height: | Size: 548 B |
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><path fill="currentColor" d="M6.346 18.25q-.234 0-.396-.162t-.161-.397t.161-.396t.396-.16h3.077l3.48-10.27H9.828q-.234 0-.396-.162t-.162-.397t.162-.395t.396-.161h7.192q.235 0 .396.162t.162.397t-.162.396q-.161.16-.396.16h-2.961l-3.481 10.27h2.962q.234 0 .395.162t.162.397t-.162.396t-.395.16z"/></svg>
|
After Width: | Height: | Size: 384 B |
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><path fill="currentColor" d="m18.52 15.758l-.77-.781q1.02-.275 1.635-1.101T20 12q0-1.27-.894-2.173q-.895-.904-2.145-.904h-3.615v-1h3.616q1.67 0 2.854 1.193T21 12q0 1.233-.69 2.23q-.689.999-1.79 1.528M15.311 12.5l-1-1h1.15v1zm5.18 9.408l-18.4-18.4L2.8 2.8l18.4 18.4zm-9.838-5.831H7.077q-1.69 0-2.884-1.193T3 12q0-1.61 1.098-2.777t2.69-1.265h.462l.966.965H7.077q-1.27 0-2.173.904Q4 10.731 4 12t.904 2.173t2.173.904h3.577zM8.539 12.5v-1h2.259l.975 1z"/></svg>
|
After Width: | Height: | Size: 541 B |
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><path fill="currentColor" d="M17.077 16.077h-2.448q-.194 0-.335-.144q-.14-.143-.14-.356q0-.194.144-.347q.143-.153.356-.153h2.423v-2.423q0-.213.144-.356q.144-.144.357-.144t.356.144t.143.356v2.423H20.5q.213 0 .356.144q.144.144.144.357t-.144.356t-.356.143h-2.423V18.5q0 .213-.144.356t-.357.144t-.356-.144t-.143-.356zm-6.961 0H7.077q-1.692 0-2.884-1.192T3 12t1.193-2.885t2.884-1.193h3.039q.194 0 .347.153t.153.357t-.153.347t-.347.143H7.075q-1.267 0-2.171.904T4 12t.904 2.173t2.17.904h3.042q.194 0 .347.153t.153.356t-.153.348t-.347.143M9 12.5q-.213 0-.356-.144t-.144-.357t.144-.356T9 11.5h6q.213 0 .356.144t.144.357t-.144.356T15 12.5zm12-.5h-1q0-1.27-.904-2.173q-.904-.904-2.17-.904H13.86q-.194 0-.335-.144t-.14-.356q0-.194.143-.347q.144-.153.357-.153h3.038q1.692 0 2.885 1.193T21 12"/></svg>
|
After Width: | Height: | Size: 872 B |
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><path fill="currentColor" d="M5.616 20q-.691 0-1.153-.462T4 18.384V5.616q0-.691.463-1.153T5.616 4h6.403v1H5.616q-.231 0-.424.192T5 5.616v12.769q0 .23.192.423t.423.192h6.404v1zm10.846-4.461l-.702-.72l2.319-2.319H9.192v-1h8.887l-2.32-2.32l.702-.718L20 12z"/></svg>
|
After Width: | Height: | Size: 347 B |
|
@ -0,0 +1,6 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img"
|
||||||
|
class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228">
|
||||||
|
<path fill="#00D8FF"
|
||||||
|
d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z">
|
||||||
|
</path>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 4.1 KiB |
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><path fill="currentColor" d="M17.085 9.673H9.342q-1.556 0-2.65 1.058q-1.096 1.058-1.096 2.596t1.095 2.606Q7.787 17 9.342 17h6.754q.214 0 .357.143t.143.357t-.143.357t-.357.143H9.342q-1.963 0-3.355-1.364t-1.39-3.309t1.39-3.3Q7.38 8.673 9.343 8.673h7.743l-2.612-2.611q-.14-.14-.15-.345q-.01-.203.15-.363t.354-.16t.354.16l3.253 3.254q.131.13.184.267t.053.298t-.053.298q-.052.137-.183.268l-3.254 3.253q-.14.14-.344.15t-.364-.15t-.16-.353t.16-.354z"/></svg>
|
After Width: | Height: | Size: 536 B |
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><path fill="currentColor" d="M8 14.5q-.213 0-.357-.143T7.5 14V9.116q0-.672.472-1.144T9.116 7.5h8.957l-2.721-2.721q-.146-.146-.156-.347t.156-.367t.357-.165t.357.166l3.369 3.369q.242.242.242.565t-.242.566l-3.37 3.388q-.14.16-.341.162t-.366-.162q-.166-.166-.159-.357t.153-.357l2.721-2.74H9.115q-.269 0-.442.173t-.173.443V14q0 .214-.143.357T8 14.5M5.616 20q-.672 0-1.144-.472T4 18.385V5.115q0-.213.143-.356t.357-.143t.357.143t.143.357v13.269q0 .269.173.442t.443.173h10.769q.269 0 .442-.173t.173-.442V15q0-.213.143-.357t.357-.143t.357.143T18 15v3.385q0 .67-.472 1.143q-.472.472-1.143.472z"/></svg>
|
After Width: | Height: | Size: 677 B |
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><path fill="currentColor" d="M12.15 19.385q-1.421 0-2.624-.751t-1.882-2.017q-.09-.188-.014-.395t.27-.297q.2-.096.418-.02t.34.276q.546.98 1.472 1.546q.926.565 2.031.565q1.389 0 2.451-.79t1.063-2.071q0-.327-.058-.654t-.23-.615q-.058-.212.061-.389q.12-.177.33-.235q.212-.057.402.062q.19.12.247.33q.212.345.279.756t.067.814q0 1.725-1.399 2.805t-3.224 1.08M3 11.5q-.213 0-.357-.143T2.5 11t.143-.357T3 10.5h18q.214 0 .357.143T21.5 11t-.143.357T21 11.5zm5.296-3.029q-.213-.058-.325-.247q-.111-.19-.054-.403q.106-1.463 1.33-2.371q1.222-.908 2.803-.908q1.066 0 2.003.452t1.566 1.287q.122.177.089.386t-.216.331q-.194.127-.412.09t-.376-.225q-.471-.617-1.163-.923t-1.48-.306q-1.194 0-2.117.67t-.998 1.782q-.057.22-.247.331t-.403.054"/></svg>
|
After Width: | Height: | Size: 814 B |
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><path fill="currentColor" d="M4.5 20q-.213 0-.356-.144T4 19.499t.144-.356T4.5 19h15q.213 0 .356.144t.144.357t-.144.356T19.5 20zm8-3.75q-.213 0-.356-.144T12 15.749t.144-.356t.356-.143h7q.213 0 .356.144t.144.357t-.144.356t-.356.143zm0-3.75q-.213 0-.356-.144T12 11.999t.144-.356t.356-.143h7q.213 0 .356.144t.144.357t-.144.356t-.356.143zm0-3.75q-.213 0-.356-.144T12 8.249t.144-.356t.356-.143h7q.213 0 .356.144t.144.357t-.144.356t-.356.143zM4.5 5q-.213 0-.356-.144T4 4.499t.144-.356T4.5 4h15q.213 0 .356.144t.144.357t-.144.356T19.5 5zm.196 9.112q-.192.192-.444.095T4 13.838v-3.676q0-.272.252-.369t.444.096l1.82 1.819q.13.123.13.288t-.13.296z"/></svg>
|
After Width: | Height: | Size: 730 B |
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><path fill="currentColor" d="M4.5 20q-.213 0-.356-.144T4 19.499t.144-.356T4.5 19h15q.213 0 .356.144t.144.357t-.144.356T19.5 20zm8-3.75q-.213 0-.356-.144T12 15.749t.144-.356t.356-.143h7q.213 0 .356.144t.144.357t-.144.356t-.356.143zm0-3.75q-.213 0-.356-.144T12 11.999t.144-.356t.356-.143h7q.213 0 .356.144t.144.357t-.144.356t-.356.143zm0-3.75q-.213 0-.356-.144T12 8.249t.144-.356t.356-.143h7q.213 0 .356.144t.144.357t-.144.356t-.356.143zM4.5 5q-.213 0-.356-.144T4 4.499t.144-.356T4.5 4h15q.213 0 .356.144t.144.357t-.144.356T19.5 5zm1.612 9.112l-1.82-1.82q-.13-.124-.13-.289t.13-.295l1.82-1.82q.192-.192.444-.095t.252.369v3.677q0 .271-.252.368t-.445-.095"/></svg>
|
After Width: | Height: | Size: 745 B |
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><path fill="currentColor" d="M6.192 19.25q-.212 0-.356-.144t-.144-.357t.144-.356t.356-.143h11.616q.212 0 .356.144t.144.357t-.144.356t-.356.143zM12 16.058q-2.14 0-3.358-1.258t-1.217-3.415V4.39q0-.233.171-.398q.171-.166.403-.166t.395.166t.164.398v7.028q0 1.631.911 2.583q.912.952 2.531.952t2.53-.952t.912-2.583V4.391q0-.233.172-.398q.17-.166.402-.166t.396.166t.163.398v6.994q0 2.157-1.217 3.415T12 16.058"/></svg>
|
After Width: | Height: | Size: 496 B |
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><path fill="currentColor" d="M7.904 18q-.214 0-.357-.143t-.143-.357t.143-.357t.357-.143h6.754q1.556 0 2.65-1.067q1.096-1.067 1.096-2.606t-1.095-2.596q-1.096-1.058-2.651-1.058H6.916l2.611 2.611q.16.16.16.354t-.16.354t-.363.15q-.204-.01-.345-.15L5.565 9.74q-.13-.131-.183-.268q-.053-.136-.053-.298t.053-.298t.184-.267l3.253-3.254q.16-.16.354-.16t.354.16t.15.363t-.15.345l-2.611 2.61h7.742q1.963 0 3.355 1.354q1.39 1.354 1.39 3.3t-1.39 3.31T14.657 18z"/></svg>
|
After Width: | Height: | Size: 542 B |
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><path fill="currentColor" d="M9.039 10H7.654q-.213 0-.356-.144t-.144-.357t.144-.356T7.654 9h1.385V7.616q0-.213.144-.357q.143-.144.356-.144t.356.144t.144.356V9h1.384q.213 0 .356.144t.144.357t-.144.356t-.356.143h-1.384v1.385q0 .212-.144.356t-.357.143t-.356-.143t-.143-.357zm.5 5.23q-2.403 0-4.067-1.662q-1.664-1.664-1.664-4.065T5.47 5.436t4.064-1.667q2.402 0 4.068 1.664q1.666 1.665 1.666 4.067q0 1.042-.369 2.017t-.97 1.668l5.909 5.907q.14.14.15.345q.009.203-.15.363q-.16.16-.354.16t-.354-.16l-5.908-5.908q-.75.639-1.725.989t-1.96.35m0-1q1.99 0 3.361-1.37q1.37-1.37 1.37-3.361T12.9 6.14T9.54 4.77q-1.991 0-3.361 1.37T4.808 9.5t1.37 3.36t3.36 1.37"/></svg>
|
After Width: | Height: | Size: 739 B |
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><path fill="currentColor" d="m19.485 20.154l-6.262-6.262q-.75.639-1.725.989t-1.96.35q-2.402 0-4.066-1.663T3.808 9.503T5.47 5.436t4.064-1.667t4.068 1.664T15.268 9.5q0 1.042-.369 2.017t-.97 1.668l6.262 6.261zM9.539 14.23q1.99 0 3.36-1.37t1.37-3.361t-1.37-3.36t-3.36-1.37t-3.361 1.37t-1.37 3.36t1.37 3.36t3.36 1.37M7.27 10V9h4.539v1z"/></svg>
|
After Width: | Height: | Size: 424 B |
|
@ -0,0 +1,7 @@
|
||||||
|
// import React from 'react'
|
||||||
|
// import ReactDOM from 'react-dom/client'
|
||||||
|
// import App from './App.tsx'
|
||||||
|
import './index.css'
|
||||||
|
import Icon from "./components/svg-icon"
|
||||||
|
|
||||||
|
export { Icon }
|
|
@ -0,0 +1,29 @@
|
||||||
|
import { ComponentProps, FC, useEffect, useRef, useState } from "react";
|
||||||
|
|
||||||
|
export const useLazySvgImport = (name: string) => {
|
||||||
|
const importRef = useRef<FC<ComponentProps<"svg">>>();
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [error, setError] = useState<Error>();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setLoading(true);
|
||||||
|
const importIcon = async () => {
|
||||||
|
try {
|
||||||
|
importRef.current = (
|
||||||
|
await import(`../icons/${name}.svg?react`)
|
||||||
|
).default; // We use `?react` here following `vite-plugin-svgr`'s convention.
|
||||||
|
} catch (err) {
|
||||||
|
setError(err as Error);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
importIcon();
|
||||||
|
}, [name]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
error,
|
||||||
|
loading,
|
||||||
|
Svg: importRef.current,
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1 @@
|
||||||
|
/// <reference types="vite/client" />
|
|
@ -0,0 +1,40 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"composite": true,
|
||||||
|
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
||||||
|
"target": "ES2020",
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"lib": [
|
||||||
|
"ES2020",
|
||||||
|
"DOM",
|
||||||
|
"DOM.Iterable"
|
||||||
|
],
|
||||||
|
"module": "ESNext",
|
||||||
|
"skipLibCheck": true,
|
||||||
|
/* Bundler mode */
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
/* Linting */
|
||||||
|
"strict": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"declaration": true,
|
||||||
|
"declarationDir": "types",
|
||||||
|
"emitDeclarationOnly": true,
|
||||||
|
"types": [
|
||||||
|
"vite-plugin-svgr/client"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"node_modules",
|
||||||
|
"dist"
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"files": [],
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.app.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.node.json"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"composite": true,
|
||||||
|
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"strict": true,
|
||||||
|
"noEmit": true
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"vite.config.ts",
|
||||||
|
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
import { IconName } from "../generated/icon-names";
|
||||||
|
interface IProps {
|
||||||
|
name: IconName;
|
||||||
|
className?: string;
|
||||||
|
svgProp?: React.SVGProps<SVGSVGElement>;
|
||||||
|
size?: 'small' | 'middle' | 'large' | number;
|
||||||
|
}
|
||||||
|
declare function Icon(props: IProps): import("react/jsx-runtime").JSX.Element;
|
||||||
|
export default Icon;
|
|
@ -0,0 +1 @@
|
||||||
|
export type IconName = 'align-center' | 'align-justify' | 'align-left' | 'align-right' | 'arrow-drop-down' | 'bold' | 'check' | 'content' | 'copy' | 'edit' | 'get-text' | 'home' | 'horizontal-rule' | 'image' | 'italic' | 'link-off' | 'link' | 'logout' | 'react' | 'redo' | 'share' | 'strike' | 'text-indent' | 'text-outdent' | 'underline' | 'undo' | 'zoomin' | 'zoomout';
|
|
@ -0,0 +1,3 @@
|
||||||
|
import './index.css';
|
||||||
|
import Icon from "./components/svg-icon";
|
||||||
|
export { Icon };
|
|
@ -0,0 +1,6 @@
|
||||||
|
import { FC } from "react";
|
||||||
|
export declare const useLazySvgImport: (name: string) => {
|
||||||
|
error: Error | undefined;
|
||||||
|
loading: boolean;
|
||||||
|
Svg: FC<import("react").SVGProps<SVGSVGElement>> | undefined;
|
||||||
|
};
|
|
@ -0,0 +1,63 @@
|
||||||
|
import { defineConfig } from 'vite'
|
||||||
|
import react from '@vitejs/plugin-react'
|
||||||
|
import svgr from "vite-plugin-svgr";
|
||||||
|
import path from 'path';
|
||||||
|
import fs from 'fs';
|
||||||
|
function generateIconTypes() {
|
||||||
|
const iconsDir = path.resolve('src/icons');
|
||||||
|
const outputPath = path.resolve('src/generated/icon-names.ts');
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: 'generate-icon-types',
|
||||||
|
buildStart() {
|
||||||
|
// Check if the icons directory exists
|
||||||
|
if (!fs.existsSync(iconsDir)) {
|
||||||
|
console.log(`Directory ${iconsDir} does not exist. Skipping icon type generation.`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the icons directory
|
||||||
|
const files = fs.readdirSync(iconsDir);
|
||||||
|
|
||||||
|
// Filter out non-SVG files and get just the base names without extension
|
||||||
|
const iconNames = files
|
||||||
|
.filter(file => file.endsWith('.svg'))
|
||||||
|
.map(file => path.basename(file, '.svg'));
|
||||||
|
|
||||||
|
// Create type definition string
|
||||||
|
const typeDefinitions = `export type IconName = ${iconNames.map(name => `'${name}'`).join(' | ')}
|
||||||
|
`;
|
||||||
|
|
||||||
|
// Write the type definitions to the output file
|
||||||
|
fs.writeFileSync(outputPath, typeDefinitions);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 在 UMD 构建模式下为外部依赖提供一个全局变量
|
||||||
|
export const GLOBALS = {
|
||||||
|
react: 'React',
|
||||||
|
'react-dom': 'ReactDOM',
|
||||||
|
};
|
||||||
|
// 处理类库使用到的外部依赖
|
||||||
|
// 确保外部化处理那些你不想打包进库的依赖
|
||||||
|
export const EXTERNAL = [
|
||||||
|
'react',
|
||||||
|
'react-dom',
|
||||||
|
];
|
||||||
|
// https://vitejs.dev/config/
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [react(), svgr(), generateIconTypes()],
|
||||||
|
build: {
|
||||||
|
rollupOptions: {
|
||||||
|
external: EXTERNAL,
|
||||||
|
output: { globals: GLOBALS },
|
||||||
|
},
|
||||||
|
lib: {
|
||||||
|
entry: path.resolve(__dirname, 'src/index.ts'),
|
||||||
|
name: 'iconer',
|
||||||
|
fileName: (format) => `index.${format}.js`,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|