Skip to content

Commit

Permalink
feat(web-components): add icons for web components (#95)
Browse files Browse the repository at this point in the history
* feat(web-components): web components icon complete🎉

* feat(web-components): web components icon complete🎉

* feat(icon): add tdesign-icons-web-components

* feat(icon): add tdesign-icons-web-components

* feat(icon): add tdesign-icons-web-components

* chore: fix typo

* feat(icon): add tdesign-icons-web-components

* feat(web-components): fix README.md

---------

Co-authored-by: wū yāng <[email protected]>
  • Loading branch information
duenyang and uyarn authored Jul 10, 2024
1 parent 59551ff commit 0f0f595
Show file tree
Hide file tree
Showing 29 changed files with 13,331 additions and 9,180 deletions.
5 changes: 3 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
},
"[typescriptreact]": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
"editor.defaultFormatter": "vscode.typescript-language-features"
},
"[javascriptreact]": {
"editor.formatOnSave": true,
Expand All @@ -26,5 +26,6 @@
"[javascript]": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
}
},
"typescript.tsdk": "node_modules/typescript/lib"
}
1 change: 1 addition & 0 deletions README-zh_CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ TDesign Icons 是统一生产、管理 TDesign 各框架的 Icons 组件资源
- [tdesign-icons-vue-next](./packages/vue-next):适用于 Vue3 的 TDesign 图标 [![npm version](https://img.shields.io/npm/v/tdesign-icons-vue-next.svg?style=flat)](https://www.npmjs.com/package/tdesign-icons-vue-next) [![NPM downloads](http://img.shields.io/npm/dm/tdesign-icons-vue-next.svg)](https://npmjs.org/package/tdesign-icons-vue-next)
- [tdesign-icons-react](./packages/react):适用于 React 的 TDesign 图标 [![npm version](https://img.shields.io/npm/v/tdesign-icons-react.svg?style=flat)](https://www.npmjs.com/package/tdesign-icons-react) [![NPM downloads](http://img.shields.io/npm/dm/tdesign-icons-react.svg)](https://npmjs.org/package/tdesign-icons-react)
- [tdesign-icons-angular](./packages/angular):适用于 Angular 的 TDesign 图标 [![npm version](https://img.shields.io/npm/v/tdesign-icons-angular.svg?style=flat)](https://www.npmjs.com/package/tdesign-icons-angular) [![NPM downloads](http://img.shields.io/npm/dm/tdesign-icons-angular.svg)](https://npmjs.org/package/tdesign-icons-angular)
- [tdesign-icons-web-components](./packages/web-components):适用于 任何框架 的 TDesign 图标 [![npm version](https://img.shields.io/npm/v/tdesign-icons-web-components.svg?style=flat)](https://www.npmjs.com/package/tdesign-icons-web-components) [![NPM downloads](https://img.shields.io/npm/dm/tdesign-icons-web-components.svg)](https://www.npmjs.com/package/tdesign-icons-web-components)
- [tdesign-icons-view](./packages/view):跨框架展示 TDesign 全量图标的 Web Component [![npm version](https://img.shields.io/npm/v/tdesign-icons-view.svg?style=flat)](https://www.npmjs.com/package/tdesign-icons-view) [![NPM downloads](http://img.shields.io/npm/dm/tdesign-icons-view.svg)](https://npmjs.org/package/tdesign-icons-view)
- [tdesign-icons-svg](./packages/svg):纯 SVG 的 TDesign 图标 [![npm version](https://img.shields.io/npm/v/tdesign-icons-svg.svg?style=flat)](https://www.npmjs.com/package/tdesign-icons-svg) [![NPM downloads](http://img.shields.io/npm/dm/tdesign-icons-svg.svg)](https://npmjs.org/package/tdesign-icons-svg)

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ TDesign Icons is a mono-repo for TDesign Icons packages and resources management
- [tdesign-icons-vue-next](./packages/vue-next):TDesign Icons for Vue 3 [![npm version](https://img.shields.io/npm/v/tdesign-icons-vue-next.svg?style=flat)](https://www.npmjs.com/package/tdesign-icons-vue-next) [![NPM downloads](http://img.shields.io/npm/dm/tdesign-icons-vue-next.svg)](https://npmjs.org/package/tdesign-icons-vue-next)
- [tdesign-icons-react](./packages/react):TDesign Icons for React [![npm version](https://img.shields.io/npm/v/tdesign-icons-react.svg?style=flat)](https://www.npmjs.com/package/tdesign-icons-react) [![NPM downloads](http://img.shields.io/npm/dm/tdesign-icons-react.svg)](https://npmjs.org/package/tdesign-icons-react)
- [tdesign-icons-angular](./packages/angular):TDesign Icons for Angular [![npm version](https://img.shields.io/npm/v/tdesign-icons-angular.svg?style=flat)](https://www.npmjs.com/package/tdesign-icons-angular) [![NPM downloads](http://img.shields.io/npm/dm/tdesign-icons-angular.svg)](https://npmjs.org/package/tdesign-icons-angular)
- [tdesign-icons-web-components](./packages/web-components):TDesign Icons for Web Components [![npm version](https://img.shields.io/npm/v/tdesign-icons-web-components.svg?style=flat)](https://www.npmjs.com/package/tdesign-icons-web-components) [![NPM downloads](https://img.shields.io/npm/dm/tdesign-icons-web-components.svg)](https://www.npmjs.com/package/tdesign-icons-web-components)
- [tdesign-icons-view](./packages/view): Web Component Package for display TDesign Icons [![npm version](https://img.shields.io/npm/v/tdesign-icons-view.svg?style=flat)](https://www.npmjs.com/package/tdesign-icons-view) [![NPM downloads](http://img.shields.io/npm/dm/tdesign-icons-view.svg)](https://npmjs.org/package/tdesign-icons-view)
- [tdesign-icons-svg](./packages/svg):SVG package of TDesign Icons [![npm version](https://img.shields.io/npm/v/tdesign-icons-svg.svg?style=flat)](https://www.npmjs.com/package/tdesign-icons-svg) [![NPM downloads](http://img.shields.io/npm/dm/tdesign-icons-svg.svg)](https://npmjs.org/package/tdesign-icons-svg)

Expand Down
37 changes: 35 additions & 2 deletions gulp/generate-icon-font.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,31 @@ import { dest, src } from 'gulp';
import concat from 'gulp-concat';
import iconfont from 'gulp-iconfont';
import iconfontCss from 'gulp-iconfont-css';
import path from 'path';
import fs from 'fs';
import { createTransformStream } from './transform';

const webComponentsFontsDir = path.resolve(__dirname, '../packages/web-components/src/iconfont/');
const webComponentsCss = `@font-face {
font-family: "t";
src: url('./t.eot'), /* for IE 9*/
url('./t.eot?#iefix') format("embedded-opentype"), /* under IE9 */
url('./t.woff') format("woff"), /* chrome, firefox */
url('./t.ttf') format("truetype"), /* opera, Safari, Android, iOS 4.2+ */
url('./t.svg') format("svg"); /* iOS 4.1- */
font-weight: normal;
font-style: normal;
}`;

const runTimestamp = Math.round(Date.now() / 1000);
const svgMap: any = {};
interface GLYPHS {
name: string;
unicode: string;
}

const iconFonts:any[] = [];

export const generateIconFont = ({
iconGlob,
targetDir,
Expand All @@ -36,11 +53,20 @@ export const generateIconFont = ({
svgMap[item.name] = item.unicode;
});
})
.pipe(dest(targetDir));
.pipe(dest(targetDir))
.on('end', () => {
// web-components 需要icon的字体文件,不需要使用cdn的方式
['t.eot', 't.svg', 't.ttf', 't.woff'].forEach((fileName) => {
fs.copyFileSync(path.resolve(targetDir, fileName), path.resolve(webComponentsFontsDir, fileName));
});
fs.writeFileSync(path.resolve(webComponentsFontsDir, 'index.css'), webComponentsCss);
});
};

function useItemJsonTemplate() {
function getItem(content: string, name: string) {
iconFonts.push({ name, codepoint: `\\${escape(svgMap[name]).replace('%u', '')}` });

return `{"name": "${name}","svgCode": ${JSON.stringify(content).replace(
/(\r\n|\n|\r)/gm,
'',
Expand All @@ -61,5 +87,12 @@ export const generateIconFontJson = ({ iconGlob, targetDir }: { iconGlob: string
.pipe(useItemJsonTemplate())
.pipe(concat('index.json'))
.pipe(useJsonTemplate())
.pipe(dest(targetDir));
.pipe(dest(targetDir))
.on('end', () => {
// web-components 需要icon的codepoint
fs.writeFileSync(
path.resolve(webComponentsFontsDir, 'font-icon.json'),
JSON.stringify(iconFonts, undefined, 2),
);
});
};
2 changes: 1 addition & 1 deletion gulp/generate-type-map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export const generateTypeMap = ({
}: {
from: string[];
to: string;
type: 'react' | 'vue';
type: 'react' | 'vue' | 'web-components';
}) => function generateManifest() {
return src(from)
.pipe(useItemTemplate())
Expand Down
2 changes: 2 additions & 0 deletions gulpfile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { svgSpriteTask } from './resources/svg-sprite/gulp';
import { iconFontTask } from './resources/icon-font/gulp';

import { iconViewTask } from './packages/view/gulp';
import { wcTask } from './packages/web-components/gulp';

const source: string[] = ['svg/*.svg'];

Expand All @@ -25,5 +26,6 @@ export default series(
svgSpriteTask(),
iconFontTask(source),
iconViewTask(),
wcTask(source),
),
);
23 changes: 23 additions & 0 deletions packages/web-components/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
.DS_Store

# auto generated icons
src/components/*.tsx
src/manifest.ts
src/icons.ts
src/global-config.ts
src/svg-sprite/svg-icon.ts
src/iconfont/font-icon.json
src/iconfont/t.*
src/iconfont/index.css

# build
lib/
dist/
esm/
es/

# yarn
yarn-error.log

# eslint
.eslintcache
1 change: 1 addition & 0 deletions packages/web-components/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
gulp/
25 changes: 25 additions & 0 deletions packages/web-components/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# TDesign Icons for Web Components

## 安装

- `npm i tdesign-icons-web-components`

## 使用

```js
// svgsprite
import 'tdesign-icons-web-components'

<t-icon name="add"></t-icon>

// iconfont
import 'tdesign-icons-web-components/esm/iconfont/index.css'
import 'tdesign-icons-web-components/esm/iconfont'

<t-icon-font name="add"></t-icon-font>

// single icon
import 'tdesign-icons-web-components/esm/components/add'

<t-icon-add></t-icon-add>
```
4 changes: 4 additions & 0 deletions packages/web-components/babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module.exports = {
presets: ['@babel/preset-env'],
plugins: ['@babel/plugin-transform-runtime'],
};
38 changes: 38 additions & 0 deletions packages/web-components/gulp/generate-icons-json.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { src } from 'gulp';

import path from 'path';
import fs from 'fs';
import { svgToElement, SvgToElementOptions } from '../../../gulp/svg-info-check';
import { svgo, SVGOConfig } from '../../../gulp/svgo';
import { useTemplate } from '../../../gulp/use-template';
import vnodeConvert from './vnode-convert';

const iconGlob = path.resolve(__dirname, '../../../svg/*.svg');
const iconDir = path.resolve(__dirname, '../../../svg');
const targetDir = path.resolve(__dirname, '../src/svg-sprite/');

interface GenerateIconOptions {
options?: SvgToElementOptions;
config?: SVGOConfig;
}

const template = fs.readFileSync(path.resolve(__dirname, 'template/iconJson.ts'), 'utf-8');

const iconsJson:Record<string, string> = {};

export const generateIconsJson = ({
config, options,
}: GenerateIconOptions) => function generateIcons() {
return src(iconGlob, { cwd: iconDir })
.pipe(svgo(config))
.pipe(svgToElement(options))
.pipe(useTemplate(({ name, element }: { name: string; element: string }) => {
const ele = JSON.stringify(vnodeConvert(JSON.parse(element)));
iconsJson[name] = ele;
return JSON.stringify(iconsJson);
}))
.on('end', () => {
const iconJson = template.replace(/\$SVGJSON/g, JSON.stringify(iconsJson, undefined, 2));
fs.writeFileSync(path.resolve(targetDir, 'svg-icon.ts'), iconJson);
});
};
53 changes: 53 additions & 0 deletions packages/web-components/gulp/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { parallel, series } from 'gulp';

import path from 'path';
import { generateEntry } from '../../../gulp/generate-entry';
import { generateManifest } from '../../../gulp/generate-manifest';
import { generateTypeMap } from '../../../gulp/generate-type-map';

import { generateIcons } from '../../../gulp/generate-icons';
import { clearDir } from '../../../gulp/clean-dir';
import { generateIconFontJson } from '../../../gulp/generate-icon-font';

import { wcGetIconFileContent } from './wc-use-template';
import { generateIconsJson } from './generate-icons-json';

const targetDir = path.resolve(__dirname, '../src/iconfont/');

export function wcTask(source: string[]) {
return series(
clearDir(['packages/web-components/src/components']),

parallel(
generateIcons({
from: source,
to: 'packages/web-components/src/components',
iconGenerator: wcGetIconFileContent,
options: {
replaceColor: true,
},
}),

generateIconsJson({
options: {
replaceColor: true,
},
}),

generateManifest({
from: source,
to: 'packages/web-components/src',
}),
generateTypeMap({
from: source,
to: 'packages/web-components/src',
type: 'web-components',
}),
),

generateEntry({
from: 'packages/web-components/src/components/*',
to: 'packages/web-components/src',
}),
);
}
19 changes: 19 additions & 0 deletions packages/web-components/gulp/template/icon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// @ts-nocheck
// This file is generated automatically by `useTemplate.ts`. DO NOT EDIT IT.

import { tag } from 'omi';
import { IconBase, IconProps } from '../icon';

const element = $ELEMENT;

@tag('$ICON_TAG_NAME')
export default class $ICON_NAME extends IconBase<IconProps> {
static icon = element;
constructor() {
super();
this.props = {
...this.props,
id: '$KEY',
}
}
}
2 changes: 2 additions & 0 deletions packages/web-components/gulp/template/iconJson.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// @ts-ignore
export default $SVGJSON;
15 changes: 15 additions & 0 deletions packages/web-components/gulp/vnode-convert.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type { VNode } from 'omi';
import type { IconElement } from '../../../gulp/svg-info-check';

export default function vnodeConvert(el: IconElement) {
const omiNode:VNode = {
attributes: el.attrs,
nodeName: el.tag,
children: [],
};
if (el?.children && el.children?.length > 0) {
omiNode.children = el.children.map(vnodeConvert);
}

return omiNode;
}
17 changes: 17 additions & 0 deletions packages/web-components/gulp/wc-use-template.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import path from 'path';
import fs from 'fs';

import { upperCamelCase } from '../../../gulp/util';
import vnodeConvert from './vnode-convert';

const template = fs.readFileSync(path.resolve(__dirname, 'template/icon.tsx'), 'utf-8');

export function wcGetIconFileContent({ name, element }: { name: string; element: string }): string {
const ele = JSON.stringify(vnodeConvert(JSON.parse(element)));

return template
.replace(/\$ICON_TAG_NAME/g, `t-icon-${name.toLocaleLowerCase()}`)
.replace(/\$ICON_NAME/g, `${upperCamelCase(name)}Icon`)
.replace(/\$ELEMENT/g, ele)
.replace(/\$KEY/g, name);
}
Loading

0 comments on commit 0f0f595

Please sign in to comment.