Fluent icon font work (#438)
* Ignore folder added by VS codespaces * Use fantasticon and fix codepoint issues * Don't generate SVG font * Handle JSON with numbers * Generate size-independent font file * Generate woff and woff2 assets * Initial groundwork for React font icons * Create font React icons * Copy font files to output * Improve styling * Create npm workspace and test app for icons * Add export map to @fluentui/react-icons * Fix icon-app * Create and copy codepoint file to be used by webpack plugin * Working prototype for subsetting webpack plugin * Update publish script * Update package-lock.json after previous package.json changes * Add importer to root workspace * Update other build definitions * Use latest Node * Update documentation * Add build script to test app * Fix flutter script * Fix minor comments from PR * Rename OneSize to Resizable Co-authored-by: tomi-msft <66456876+tomi-msft@users.noreply.github.com>
This commit is contained in:
Родитель
f615657b9a
Коммит
8b56c88328
|
@ -8,13 +8,12 @@ steps:
|
|||
|
||||
- task: UseNode@1
|
||||
inputs:
|
||||
version: '11.x'
|
||||
version: '18.x'
|
||||
|
||||
- task: Npm@1
|
||||
displayName: Run npm install
|
||||
inputs:
|
||||
command: 'install'
|
||||
workingDir: 'importer'
|
||||
|
||||
- task: Npm@1
|
||||
displayName: Run generate script
|
||||
|
|
|
@ -8,13 +8,12 @@ steps:
|
|||
|
||||
- task: UseNode@1
|
||||
inputs:
|
||||
version: '11.x'
|
||||
version: '18.x'
|
||||
|
||||
- task: Npm@1
|
||||
displayName: Run npm install
|
||||
inputs:
|
||||
command: 'install'
|
||||
workingDir: 'importer'
|
||||
|
||||
- task: Npm@1
|
||||
displayName: Run generate script
|
||||
|
|
|
@ -65,6 +65,12 @@ jobs:
|
|||
sed -i.bk -r "s/\"version\": \"[0-9]+\.[0-9]+\.[0-9]+\"/\"version\": \"$NEW_VERSION\"/g" packages/react-icons/package.json
|
||||
rm packages/react-icons/package.json.bk
|
||||
|
||||
# Needs to be "-E" instead of "-r" on macOS
|
||||
- name: Replace version number in react-icons-font-subsetting-webpack-plugin/package.json
|
||||
run: |
|
||||
sed -i.bk -r "s/\"version\": \"[0-9]+\.[0-9]+\.[0-9]+\"/\"version\": \"$NEW_VERSION\"/g" packages/react-icons-font-subsetting-webpack-plugin/package.json
|
||||
rm packages/react-icons-font-subsetting-webpack-plugin/package.json.bk
|
||||
|
||||
- name: Config git credentials
|
||||
run: git config user.email "flubuild@microsoft.com" && git config user.name "Fluent Build System"
|
||||
|
||||
|
|
|
@ -33,13 +33,12 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Use Node 11
|
||||
- name: Use Node 18
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 11.x
|
||||
node-version: 18.x
|
||||
|
||||
- run: npm install
|
||||
working-directory: importer
|
||||
|
||||
- name: Run generate script
|
||||
run: npm run deploy:ios
|
||||
|
@ -52,13 +51,12 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Use Node 11
|
||||
- name: Use Node 18
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 11.x
|
||||
node-version: 18.x
|
||||
|
||||
- run: npm install
|
||||
working-directory: importer
|
||||
|
||||
- name: Run generate script
|
||||
run: npm run deploy:android
|
||||
|
@ -88,13 +86,12 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Use Node 11
|
||||
- name: Use Node 18
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 11.x
|
||||
node-version: 18.x
|
||||
|
||||
- run: npm install
|
||||
working-directory: importer
|
||||
|
||||
- name: Run generate script
|
||||
run: npm run deploy:flutter
|
||||
|
@ -119,16 +116,12 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Use Node 11
|
||||
- name: Use Node 18
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 11.x
|
||||
node-version: 18.x
|
||||
|
||||
- run: npm install
|
||||
working-directory: importer
|
||||
|
||||
- run: npm install
|
||||
working-directory: packages/svg-icons
|
||||
|
||||
- name: Run build
|
||||
run: npm run build
|
||||
|
@ -141,15 +134,21 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Use Node 11
|
||||
- name: Use Node 18
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 11.x
|
||||
node-version: 18.x
|
||||
|
||||
- run: npm install
|
||||
working-directory: importer
|
||||
|
||||
- run: |
|
||||
npm install
|
||||
npm run build
|
||||
working-directory: packages/react-icons
|
||||
|
||||
- run: |
|
||||
npm run build
|
||||
working-directory: packages/react-icons-font-subsetting-webpack-plugin
|
||||
|
||||
- run: |
|
||||
npm run test
|
||||
working-directory: packages/react-icons-font-subsetting-webpack-plugin
|
||||
|
|
|
@ -43,10 +43,10 @@ jobs:
|
|||
os.system(f'echo \"REACT_VERSION={major}.{minor}.{int(asset) + 1}-rc.{int(rc) + 1}\" >> \$GITHUB_ENV')
|
||||
"""
|
||||
|
||||
- name: Use Node 11
|
||||
- name: Use Node 18
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 11.x
|
||||
node-version: 18.x
|
||||
|
||||
- run: npm install
|
||||
working-directory: importer
|
||||
|
@ -113,18 +113,30 @@ jobs:
|
|||
sed -i.bk -r "s/\"version\": \"[0-9]+\.[0-9]+\.[0-9]+(-beta\.[0-9]+)?(-rc\.[0-9]+)?\"/\"version\": \"$REACT_VERSION\"/g" packages/react-icons/package.json
|
||||
rm packages/react-icons/package.json.bk
|
||||
|
||||
# Needs to be "-E" instead of "-r" on macOS
|
||||
- name: Replace version number in react-icons-font-subsetting-webpack-plugin/package.json
|
||||
run: |
|
||||
sed -i.bk -r "s/\"version\": \"[0-9]+\.[0-9]+\.[0-9]+(-beta\.[0-9]+)?(-rc\.[0-9]+)?\"/\"version\": \"$REACT_VERSION\"/g" packages/react-icons-font-subsetting-webpack-plugin/package.json
|
||||
rm packages/react-icons-font-subsetting-webpack-plugin/package.json.bk
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm install
|
||||
|
||||
- name: Build SVG library
|
||||
run: |
|
||||
npm install
|
||||
npm run build
|
||||
working-directory: packages/svg-icons
|
||||
|
||||
- name: Build React library
|
||||
run: |
|
||||
npm install
|
||||
npm run build
|
||||
working-directory: packages/react-icons
|
||||
|
||||
- name: Build Webpack plugin library
|
||||
run: |
|
||||
npm run build
|
||||
working-directory: packages/react-icons-font-subsetting-webpack-plugin
|
||||
|
||||
- uses: JS-DevTools/npm-publish@v1
|
||||
with:
|
||||
token: ${{ secrets.NPM_TOKEN }}
|
||||
|
@ -138,6 +150,13 @@ jobs:
|
|||
package: packages/react-icons/package.json
|
||||
tag: rc
|
||||
|
||||
- uses: JS-DevTools/npm-publish@v1
|
||||
with:
|
||||
token: ${{ secrets.NPM_TOKEN }}
|
||||
access: public
|
||||
package: packages/react-icons-font-subsetting-webpack-plugin/package.json
|
||||
tag: rc
|
||||
|
||||
## Android
|
||||
- name: Run Android generate script
|
||||
env:
|
||||
|
|
|
@ -30,4 +30,6 @@ packages/react-icons/yarn.lock
|
|||
packages/react-icons/package-lock.json
|
||||
|
||||
# Some Fluent tooling generates this for internal tracking assistance. Make sure it doesn't get committed.
|
||||
SvgList.txt
|
||||
SvgList.txt
|
||||
|
||||
.venv
|
|
@ -0,0 +1,107 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
// @ts-check
|
||||
|
||||
const fs = require("fs/promises");
|
||||
const mkdirp = require('mkdirp');
|
||||
const path = require("path");
|
||||
const { promisify } = require('util');
|
||||
const glob = promisify(require('glob'));
|
||||
const process = require("process");
|
||||
const argv = require("yargs").boolean("selector").default("selector", false).argv;
|
||||
const _ = require("lodash");
|
||||
const fantasticon = require('fantasticon');
|
||||
|
||||
const SRC_PATH = argv.source;
|
||||
const DEST_PATH = argv.dest;
|
||||
const ICON_TYPE = argv.iconType;
|
||||
const CODEPOINTS_FILE = argv.codepoints;
|
||||
|
||||
if (!SRC_PATH) {
|
||||
throw new Error("SVG source folder not specified by --source");
|
||||
}
|
||||
if (!DEST_PATH) {
|
||||
throw new Error("Output destination folder not specified by --dest");
|
||||
}
|
||||
if (!(ICON_TYPE === 'Filled' || ICON_TYPE === 'Regular' || ICON_TYPE === 'Resizable')) {
|
||||
throw new Error("Icon type not specified");
|
||||
}
|
||||
|
||||
async function main() {
|
||||
await mkdirp(DEST_PATH);
|
||||
const stagingFolder = path.resolve(DEST_PATH, ICON_TYPE);
|
||||
await mkdirp(stagingFolder);
|
||||
|
||||
const svgFiles = await glob(path.resolve(SRC_PATH, `*_${ICON_TYPE === 'Resizable' ? '20_{filled,regular}' : ICON_TYPE.toLowerCase()}.svg`));
|
||||
const icons = new Set(svgFiles.map(file => path.basename(file).replace(/\.svg$/, '')));
|
||||
|
||||
if (icons.size > 6400) {
|
||||
throw new Error('Too many icons to fit into the Unicode private use area (0xE000-0xF8FF). See https://unicode-table.com/en/blocks/private-use-area/')
|
||||
}
|
||||
|
||||
// Copy all icons of the given icon type to the staging folder
|
||||
await Promise.all((svgFiles).map(
|
||||
async svgFile => fs.copyFile(svgFile, path.resolve(stagingFolder, path.basename(svgFile)))
|
||||
));
|
||||
|
||||
// Generate the font and associated assets
|
||||
await fantasticon.generateFonts({
|
||||
inputDir: stagingFolder,
|
||||
outputDir: path.resolve(DEST_PATH),
|
||||
name: `FluentSystemIcons-${ICON_TYPE}`,
|
||||
fontTypes: [fantasticon.ASSET_TYPES.WOFF2, fantasticon.ASSET_TYPES.WOFF, fantasticon.ASSET_TYPES.TTF],
|
||||
assetTypes: [fantasticon.ASSET_TYPES.CSS, fantasticon.ASSET_TYPES.HTML, fantasticon.ASSET_TYPES.JSON],
|
||||
formatOptions: { json: { indent: 2 } },
|
||||
codepoints: await getCodepoints(icons),
|
||||
fontHeight: 500,
|
||||
normalize: true
|
||||
});
|
||||
|
||||
// Clean up staging folder
|
||||
await Promise.all(svgFiles.map(
|
||||
async svgFile => fs.unlink(path.resolve(stagingFolder, path.basename(svgFile)))
|
||||
));
|
||||
if ((await fs.readdir(stagingFolder)).length === 0) {
|
||||
await fs.rmdir(stagingFolder);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Set<string>} icons - Set of icons being consumed into the font
|
||||
* @returns {Promise<Record<string, number>>}
|
||||
*/
|
||||
async function getCodepoints(icons) {
|
||||
/** @type {Record<string, number>} */
|
||||
let codepoints = {};
|
||||
if (CODEPOINTS_FILE) {
|
||||
const originalCodepoints = JSON.parse(await fs.readFile(CODEPOINTS_FILE, 'utf8'));
|
||||
codepoints = Object.fromEntries(
|
||||
Object.entries(originalCodepoints)
|
||||
.filter(([iconName]) => icons.has(iconName))
|
||||
.map(([iconName, codepoint]) => [iconName, typeof codepoint === 'number' ? codepoint : Number.parseInt(codepoint)])
|
||||
);
|
||||
}
|
||||
|
||||
// Fix any codepoints outside the private use area
|
||||
let nextCodePoint = 0xe000;
|
||||
let usedCodePoints = new Set(Object.values(codepoints));
|
||||
|
||||
for (const iconName of icons) {
|
||||
const originalCodepoint = codepoints[iconName]
|
||||
if (!originalCodepoint || originalCodepoint < 0xe000 || originalCodepoint > 0xf8ff) {
|
||||
// Find a new free codepoint
|
||||
while (usedCodePoints.has(nextCodePoint)) {
|
||||
nextCodePoint++;
|
||||
}
|
||||
|
||||
usedCodePoints.add(nextCodePoint);
|
||||
codepoints[iconName] = nextCodePoint;
|
||||
}
|
||||
}
|
||||
|
||||
return codepoints;
|
||||
|
||||
}
|
||||
|
||||
main();
|
|
@ -88,7 +88,7 @@ function writeCodeForJson(srcPath, iconClassFile, rtlIcons) {
|
|||
let style = match[3];
|
||||
FILE_NAME_REGEX.lastIndex = 0;
|
||||
|
||||
let codepoint = orderedJsonData[fullName].replace("\\", "0x");
|
||||
let codepoint = orderedJsonData[fullName];
|
||||
let identifier = `${name}_${size}_${style}`;
|
||||
let matchTextDirection = rtlIcons.includes(fullName) ? `, matchTextDirection: true` : "";
|
||||
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -19,9 +19,10 @@
|
|||
"deploy:android": "npm run build:android && rm -rf ../android/library/src/main/res/drawable && mkdir ../android/library/src/main/res/drawable && find ./dist/ -type f -name \"*.xml\" -maxdepth 1 -exec cp {} ../android/library/src/main/res/drawable \\; && npm run clean",
|
||||
"deploy:react": "npm run build:react && rm -rf ../react/src/components && mkdir ../react/src/components && find ./dist/ -type f -name \"*.tsx\" -exec cp {} ../react/src/components \\; && npm run clean",
|
||||
"deploy:ios": "npm run build:ios && python3 process_ios_assets.py && npm run clean",
|
||||
"generate:font-regular": "icon-font-generator dist/*_regular.svg -o dist/fonts --name FluentSystemIcons-Regular --types ttf --codepoints ../fonts/FluentSystemIcons-Regular.json --height 500",
|
||||
"generate:font-filled": "icon-font-generator dist/*_filled.svg -o dist/fonts --name FluentSystemIcons-Filled --types ttf --codepoints ../fonts/FluentSystemIcons-Filled.json --height 500",
|
||||
"build:fonts": "npm run generate:svg && find ./dist -type f -name '*.svg' -exec svgo --config svgo_config.yml {} + && mkdir dist/fonts && npm run generate:font-regular && npm run generate:font-filled && replace '\\\\\\\\' '0x' dist/fonts/*.json",
|
||||
"generate:font-regular": "node generateFont.js --source=dist --dest=dist/fonts --iconType=Regular --codepoints=../fonts/FluentSystemIcons-Regular.json",
|
||||
"generate:font-filled": "node generateFont.js --source=dist --dest=dist/fonts --iconType=Filled --codepoints=../fonts/FluentSystemIcons-Filled.json",
|
||||
"generate:font-resizable": "node generateFont.js --source=dist --dest=dist/fonts --iconType=Resizable",
|
||||
"build:fonts": "npm run generate:svg && find ./dist -type f -name '*.svg' -exec svgo --config svgo_config.yml {} + && mkdir dist/fonts && npm run generate:font-regular && npm run generate:font-filled && npm run generate:font-resizable && replace '\\\\\\\\' '0x' dist/fonts/*.json",
|
||||
"deploy:fonts": "npm run build:fonts && cp -a dist/fonts/* ../fonts && npm run clean",
|
||||
"generate:flutter-icon-lib-class": "node generate_flutter_lib_class.js --source=../fonts/FluentSystemIcons-Regular.json ../fonts/FluentSystemIcons-Filled.json --dest=dist/flutter",
|
||||
"generate:flutter-icon-demo-class": "node generate_flutter_demo_class.js --source=../fonts/FluentSystemIcons-Regular.json ../fonts/FluentSystemIcons-Filled.json --dest=dist/flutter",
|
||||
|
@ -32,8 +33,10 @@
|
|||
"license": "Microsoft",
|
||||
"devDependencies": {
|
||||
"avocado": "1.0.0",
|
||||
"icon-font-generator": "^2.1.10",
|
||||
"fantasticon": "^1.2.3",
|
||||
"glob": "^8.0.1",
|
||||
"lodash": "4.17.21",
|
||||
"mkdirp": "^1.0.4",
|
||||
"react": "~17.0.1",
|
||||
"replace": "^1.2.0",
|
||||
"shx": "^0.3.2",
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"name": "fluent-system-icons",
|
||||
"private": true,
|
||||
"workspaces": [
|
||||
"packages/*",
|
||||
"importer"
|
||||
],
|
||||
"description": "![CI](https://github.com/microsoft/fluentui-system-icons/workflows/CI/badge.svg)",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/microsoft/fluentui-system-icons.git"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"name": "icon-app",
|
||||
"version": "1.0.0",
|
||||
"description": "Test/demo app for @fluentui/react-icons",
|
||||
"main": "index.js",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "webpack"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@types/react": "^17.0.2",
|
||||
"@types/react-dom": "^17.0.0",
|
||||
"html-webpack-plugin": "^5.5.0",
|
||||
"ts-loader": "^9.2.9",
|
||||
"typescript": "^4.6.3",
|
||||
"webpack": "^5.72.0",
|
||||
"webpack-cli": "^4.9.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fluentui/react-icons": "*",
|
||||
"@griffel/react": "^1.0.3",
|
||||
"react": "^17.0.1",
|
||||
"react-dom": "^17.0.1"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
import * as React from 'react';
|
||||
import * as ReactDOM from 'react-dom';
|
||||
import * as fontsModule from '@fluentui/react-icons/lib/fonts';
|
||||
// @ts-ignore
|
||||
import * as svgModule from '@fluentui/react-icons/lib/svg';
|
||||
import { makeStyles } from "@griffel/react";
|
||||
|
||||
const fontComponents = filterModuleImports(fontsModule);
|
||||
const svgComponents = filterModuleImports(svgModule);
|
||||
|
||||
function main() {
|
||||
const rootDiv = document.createElement('div');
|
||||
document.body.append(rootDiv);
|
||||
|
||||
ReactDOM.render(<MainComponent />, rootDiv);
|
||||
|
||||
|
||||
}
|
||||
|
||||
const useRootStyles = makeStyles({
|
||||
root: {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
function MainComponent() {
|
||||
const styles = useRootStyles();
|
||||
return (<div className={styles.root}>
|
||||
{Object.keys(fontComponents).map(name => <IconCell FontIcon={fontComponents[name]} SvgIcon={svgComponents[name]} name={name} key={name} />)}
|
||||
</div>)
|
||||
}
|
||||
|
||||
const useCellStyles = makeStyles({
|
||||
root: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyItems: 'end',
|
||||
height: '120px',
|
||||
width: '120px',
|
||||
backgroundColor: "#ccc",
|
||||
marginLeft: '5px',
|
||||
marginRight: '5px',
|
||||
marginTop: '5px',
|
||||
marginBottom: '5px'
|
||||
},
|
||||
iconZone: {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
justifyItems: 'center'
|
||||
},
|
||||
iconCell: {
|
||||
backgroundColor: '#fff',
|
||||
flexBasis: '50%',
|
||||
flexShrink: '1',
|
||||
flexGrow: '1',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyItems: 'end',
|
||||
fontSize: '40px',
|
||||
marginLeft: '5px',
|
||||
marginRight: '5px',
|
||||
marginTop: '5px',
|
||||
marginBottom: '5px'
|
||||
},
|
||||
sublabel: {
|
||||
display: 'inline-block',
|
||||
fontSize: '14px',
|
||||
height: '20px'
|
||||
},
|
||||
mainLabel: {
|
||||
fontSize: '14px',
|
||||
overflowX: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
marginLeft: '5px',
|
||||
marginRight: '5px',
|
||||
marginTop: '5px',
|
||||
marginBottom: '5px'
|
||||
}
|
||||
});
|
||||
|
||||
function IconCell({ FontIcon, SvgIcon, name }: { FontIcon: React.ComponentType, SvgIcon: React.ComponentType, name: string }) {
|
||||
const styles = useCellStyles()
|
||||
return <div className={styles.root}>
|
||||
<div className={styles.iconZone}>
|
||||
<div className={styles.iconCell}><FontIcon /><span className={styles.sublabel}>font</span></div>
|
||||
<div className={styles.iconCell}><SvgIcon /><span className={styles.sublabel}>svg</span></div>
|
||||
</div>
|
||||
<span className={styles.mainLabel} title={name}>{name}</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
function filterModuleImports(mod: Record<string, unknown>): Record<string, React.ComponentType> {
|
||||
const importsToFilter = new Set(['wrapIcon', 'bundleIcon', 'useIconState', 'iconFilledClassName', 'iconRegularClassName', '_esModule']);
|
||||
const components: Record<string, React.ComponentType> = {};
|
||||
|
||||
for (const [name, possibleComponent] of Object.entries(mod)) {
|
||||
if (!importsToFilter.has(name)) {
|
||||
components[name] = possibleComponent as React.ComponentType;
|
||||
}
|
||||
}
|
||||
|
||||
return components;
|
||||
}
|
||||
|
||||
main();
|
|
@ -0,0 +1,101 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
/* Visit https://aka.ms/tsconfig.json to read more about this file */
|
||||
|
||||
/* Projects */
|
||||
// "incremental": true, /* Enable incremental compilation */
|
||||
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
|
||||
// "tsBuildInfoFile": "./", /* Specify the folder for .tsbuildinfo incremental compilation files. */
|
||||
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects */
|
||||
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
|
||||
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
|
||||
|
||||
/* Language and Environment */
|
||||
"target": "es2020", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
|
||||
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
|
||||
"jsx": "react", /* Specify what JSX code is generated. */
|
||||
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
|
||||
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
|
||||
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */
|
||||
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
|
||||
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */
|
||||
// "reactNamespace": "", /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */
|
||||
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
|
||||
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
|
||||
|
||||
/* Modules */
|
||||
"module": "esnext", /* Specify what module code is generated. */
|
||||
// "rootDir": "./src", /* Specify the root folder within your source files. */
|
||||
"moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
|
||||
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
||||
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
|
||||
"rootDirs": ["./src", "../react-icons/src"], /* Allow multiple folders to be treated as one when resolving modules. */
|
||||
// "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */
|
||||
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
|
||||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||
// "resolveJsonModule": true, /* Enable importing .json files */
|
||||
// "noResolve": true, /* Disallow `import`s, `require`s or `<reference>`s from expanding the number of files TypeScript should add to a project. */
|
||||
|
||||
/* JavaScript Support */
|
||||
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */
|
||||
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
|
||||
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */
|
||||
|
||||
/* Emit */
|
||||
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
|
||||
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
|
||||
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
|
||||
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
|
||||
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */
|
||||
"outDir": "./lib", /* Specify an output folder for all emitted files. */
|
||||
// "removeComments": true, /* Disable emitting comments. */
|
||||
// "noEmit": true, /* Disable emitting files from a compilation. */
|
||||
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
|
||||
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types */
|
||||
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
|
||||
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
|
||||
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
|
||||
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
|
||||
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
|
||||
// "newLine": "crlf", /* Set the newline character for emitting files. */
|
||||
// "stripInternal": true, /* Disable emitting declarations that have `@internal` in their JSDoc comments. */
|
||||
// "noEmitHelpers": true, /* Disable generating custom helper functions like `__extends` in compiled output. */
|
||||
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
|
||||
// "preserveConstEnums": true, /* Disable erasing `const enum` declarations in generated code. */
|
||||
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
|
||||
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
|
||||
|
||||
/* Interop Constraints */
|
||||
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
|
||||
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
|
||||
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */
|
||||
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
|
||||
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
|
||||
|
||||
/* Type Checking */
|
||||
"strict": true, /* Enable all strict type-checking options. */
|
||||
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied `any` type.. */
|
||||
// "strictNullChecks": true, /* When type checking, take into account `null` and `undefined`. */
|
||||
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
|
||||
// "strictBindCallApply": true, /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */
|
||||
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
|
||||
// "noImplicitThis": true, /* Enable error reporting when `this` is given the type `any`. */
|
||||
// "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */
|
||||
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
|
||||
// "noUnusedLocals": true, /* Enable error reporting when a local variables aren't read. */
|
||||
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read */
|
||||
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
|
||||
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
|
||||
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
|
||||
// "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */
|
||||
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
|
||||
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type */
|
||||
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
|
||||
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
|
||||
|
||||
/* Completeness */
|
||||
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
|
||||
"skipLibCheck": true /* Skip type checking all .d.ts files. */
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
|
||||
module.exports = {
|
||||
resolve: {
|
||||
// Add `.ts` and `.tsx` as a resolvable extension.
|
||||
extensions: [".ts", ".tsx", ".js"]
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
// all files with a `.ts` or `.tsx` extension will be handled by `ts-loader`
|
||||
{ test: /\.tsx?$/, loader: "ts-loader" },
|
||||
{
|
||||
test: /\.(ttf|woff2?)$/,
|
||||
type: 'asset',
|
||||
}
|
||||
]
|
||||
},
|
||||
plugins: [
|
||||
new HtmlWebpackPlugin()
|
||||
]
|
||||
};
|
|
@ -0,0 +1 @@
|
|||
lib/
|
|
@ -0,0 +1,33 @@
|
|||
@fluentui/react-icons-font-subsetting-webpack-plugin
|
||||
===
|
||||
|
||||
This package includes a plugin for `webpack@>=5.0.0` to subset the icon font files used by `@fluentui/react-icons` when using the `"fluentIcontFont"` condition in `resolve.conditionNames`.
|
||||
|
||||
If `optimization.usedExports` is enabled (as it is by default), this plugin will subset the font files to only include the glyphs actually used by your build.
|
||||
|
||||
Usage
|
||||
---
|
||||
```js
|
||||
// webpack.config.js
|
||||
const {default: FluentUIReactIconsFontSubsettingPlugin} = require('@fluentui/react-icons-font-subsetting-webpack-plugin');
|
||||
|
||||
module.exports = {
|
||||
module: {
|
||||
rules: [
|
||||
// Treat the font files as webpack assets
|
||||
{
|
||||
test: /\.(ttf|woff2?)$/,
|
||||
type: 'asset',
|
||||
}
|
||||
]
|
||||
},
|
||||
resolve: {
|
||||
// Include 'fluentIcontFont' to use the font implementation of the Fluent icons
|
||||
conditionNames: ['fluentIcontFont', 'import']
|
||||
},
|
||||
plugins: [
|
||||
// Include this plugin
|
||||
new FluentUIReactIconsFontSubsettingPlugin(),
|
||||
],
|
||||
};
|
||||
```
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"name": "@fluentui/react-icons-font-subsetting-webpack-plugin",
|
||||
"version": "1.0.0",
|
||||
"description": "Webpack plugin to subset the icon fonts used by @fluentui/react-icons based on which icons are used.",
|
||||
"main": "lib/index.js",
|
||||
"scripts": {
|
||||
"test": "webpack -c test/webpack.config.js",
|
||||
"build": "tsc"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.5.0"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/microsoft/fluentui-system-icons.git",
|
||||
"directory": "packages/react-icons-font-subsetting-webpack-plugin"
|
||||
},
|
||||
"author": "Michael Loughry <miclo@microsoft.com>",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/microsoft/fluentui-system-icons/issues"
|
||||
},
|
||||
"homepage": "https://github.com/microsoft/fluentui-system-icons/packages/react-icons-font-subsetting-webpack-plugin#readme",
|
||||
"devDependencies": {
|
||||
"typescript": "^4.6.3",
|
||||
"webpack": "^5.72.0",
|
||||
"@fluentui/react-icons": "*"
|
||||
},
|
||||
"dependencies": {
|
||||
"subset-font": "^1.4.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"webpack": ">=5.0.0"
|
||||
},
|
||||
"files": [
|
||||
"lib/*"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,132 @@
|
|||
import * as webpack from "webpack";
|
||||
import subsetFont from "subset-font";
|
||||
import { extname, dirname, resolve } from "path";
|
||||
import { readFile } from 'fs/promises';
|
||||
|
||||
const PLUGIN_NAME = "FluentUIReactIconsFontSubsettingPlugin";
|
||||
|
||||
const FONT_FILES_BASE_NAMES = [
|
||||
"FluentSystemIcons-Filled",
|
||||
"FluentSystemIcons-Resizable",
|
||||
"FluentSystemIcons-Regular",
|
||||
];
|
||||
|
||||
const FONT_EXTENSIONS = [
|
||||
'.ttf',
|
||||
'.woff',
|
||||
'.woff2'
|
||||
]
|
||||
|
||||
export default class FluentUIReactIconsFontSubsettingPlugin
|
||||
implements webpack.WebpackPluginInstance {
|
||||
|
||||
apply(compiler: webpack.Compiler) {
|
||||
compiler.hooks.compilation.tap(
|
||||
PLUGIN_NAME,
|
||||
(compilation: webpack.Compilation) => {
|
||||
|
||||
compilation.hooks.optimizeAssets.tapPromise(PLUGIN_NAME, async () => {
|
||||
// There could be multiple instances of `@fluentui/react-icons`, and they need to be subset separately
|
||||
const packageToUsedFontExports: Map<string, Set<string>> = new Map<string, Set<string>>();
|
||||
for (const m of compilation.modules) {
|
||||
if (isFluentUIReactFontChunk(m)) {
|
||||
const usedModuleExports = compilation.moduleGraph.getUsedExports(m, undefined);
|
||||
|
||||
if (usedModuleExports === null || typeof usedModuleExports === 'boolean') {
|
||||
// Either all exports are used or there's no info on used exports
|
||||
continue;
|
||||
}
|
||||
|
||||
const pkgLibPath = resolve(dirname(m.resource), '../..');
|
||||
|
||||
const usedPkgExports = packageToUsedFontExports.get(pkgLibPath) ?? new Set<string>();
|
||||
for (const icon of usedModuleExports) {
|
||||
usedPkgExports.add(icon);
|
||||
}
|
||||
packageToUsedFontExports.set(pkgLibPath, usedPkgExports);
|
||||
}
|
||||
}
|
||||
const optimizationPromises: Promise<void>[] = [];
|
||||
|
||||
for (const [pkgLibPath, usedExports] of packageToUsedFontExports) {
|
||||
for (const { assetName, codepoints: codepointMap } of await getFontAssetsAndCodepoints(pkgLibPath, compilation)) {
|
||||
optimizationPromises.push(optimizeFontAsset(codepointMap, usedExports, compilation, assetName));
|
||||
}
|
||||
}
|
||||
|
||||
await optimizationPromises;
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async function optimizeFontAsset(codepointMap: Record<string, number>, usedExports: Set<string>, compilation: webpack.Compilation, assetName: string) {
|
||||
const subsetText = Object.entries(codepointMap)
|
||||
.filter(([glyphName]) => usedExports.has(glyphName))
|
||||
.map(([, codepoint]) => String.fromCodePoint(codepoint))
|
||||
.join('');
|
||||
|
||||
|
||||
let source = compilation.assets[assetName].source();
|
||||
|
||||
if (typeof source === "string") {
|
||||
source = Buffer.from(source);
|
||||
}
|
||||
|
||||
compilation.assets[assetName] = new webpack.sources.RawSource(
|
||||
await subsetFont(
|
||||
source,
|
||||
subsetText,
|
||||
{
|
||||
targetFormat: getTargetFormat(assetName),
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function getTargetFormat(assetName: string) {
|
||||
switch (extname(assetName)) {
|
||||
case ".woff":
|
||||
return "woff";
|
||||
case ".woff2":
|
||||
return "woff2";
|
||||
default:
|
||||
return "sfnt";
|
||||
}
|
||||
}
|
||||
|
||||
function isNormalModule(m: webpack.Module): m is webpack.NormalModule {
|
||||
return m instanceof webpack.NormalModule;
|
||||
}
|
||||
|
||||
function isFluentUIReactFontChunk(m: webpack.Module): m is webpack.NormalModule {
|
||||
if (isNormalModule(m)) {
|
||||
return /react-icons[\/\\]lib(-cjs)?[\/\\]fonts[\/\\](sizedIcons|icons)[\/\\]chunk-\d+\.js$/.test(m.resource)
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
async function getFontAssetsAndCodepoints(pkgLibPath: string, compilation: webpack.Compilation): Promise<{ assetName: string, codepoints: Record<string, number> }[]> {
|
||||
const utilsFontsFolder = resolve(pkgLibPath, 'utils/fonts');
|
||||
const codepoints: Record<string, Record<string, number>> = Object.fromEntries(await Promise.all(
|
||||
FONT_FILES_BASE_NAMES.map(async fontBaseName => [fontBaseName, JSON.parse(await readFile(resolve(utilsFontsFolder, `${fontBaseName}.json`), 'utf8'))])
|
||||
))
|
||||
const fontPaths = new Map<string, Record<string, number>>(
|
||||
FONT_FILES_BASE_NAMES.flatMap(fontBaseName => FONT_EXTENSIONS.map(ext => [resolve(utilsFontsFolder, `${fontBaseName}${ext}`), codepoints[fontBaseName]])
|
||||
));
|
||||
|
||||
const result: { assetName: string, codepoints: Record<string, number> }[] = [];
|
||||
|
||||
for (const m of compilation.modules) {
|
||||
if (isNormalModule(m) && fontPaths.has(m.resource)) {
|
||||
const assetName = m.buildInfo?.filename;
|
||||
if (assetName) {
|
||||
result.push({ assetName, codepoints: fontPaths.get(m.resource)! })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
13
packages/react-icons-font-subsetting-webpack-plugin/src/subset-font.d.ts
поставляемый
Normal file
13
packages/react-icons-font-subsetting-webpack-plugin/src/subset-font.d.ts
поставляемый
Normal file
|
@ -0,0 +1,13 @@
|
|||
// Typings for the `subset-font` npm package
|
||||
declare module "subset-font" {
|
||||
function subsetFont(
|
||||
buffer: Buffer,
|
||||
text: string,
|
||||
options?: {
|
||||
targetFormat?: "sfnt" | "woff" | "woff2";
|
||||
preserveNameIds?: boolean;
|
||||
}
|
||||
): Promise<Buffer>;
|
||||
export default subsetFont;
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
import {XboxConsole24Filled, BoardGames20Regular, GamesFilled} from '@fluentui/react-icons';
|
||||
|
||||
console.dir({
|
||||
XboxConsole24Filled,
|
||||
BoardGames20Regular,
|
||||
GamesFilled
|
||||
})
|
|
@ -0,0 +1,41 @@
|
|||
const {default: FluentUIReactIconsFontSubsettingPlugin} = require('../lib/');
|
||||
const {resolve} = require('path');
|
||||
|
||||
const FONT_THRESHOLD_SIZE = 2 * 1_024; // 2kb
|
||||
|
||||
module.exports = {
|
||||
context: __dirname,
|
||||
mode: 'production',
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.(ttf|woff2?)$/,
|
||||
type: 'asset',
|
||||
generator: {
|
||||
filename: `[name]-[contenthash][ext]`,
|
||||
dataUrl: {},
|
||||
},
|
||||
}
|
||||
]
|
||||
},
|
||||
output: {
|
||||
path: resolve(__dirname, 'dist'),
|
||||
},
|
||||
resolve: {
|
||||
conditionNames: ['fluentIcontFont', 'import']
|
||||
},
|
||||
plugins: [
|
||||
new FluentUIReactIconsFontSubsettingPlugin(),
|
||||
{
|
||||
apply(compiler) {
|
||||
compiler.hooks.afterEmit.tap('test-subsetting', (compilation) => {
|
||||
for (const [assetName, source] of Object.entries(compilation.assets)) {
|
||||
if (/\.(ttf|woff2?)$/.test(assetName) && source.size() > FONT_THRESHOLD_SIZE) {
|
||||
throw new Error(`Asset "${assetName}" does not appear to have been properly subset.`)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
],
|
||||
};
|
|
@ -0,0 +1,101 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
/* Visit https://aka.ms/tsconfig.json to read more about this file */
|
||||
|
||||
/* Projects */
|
||||
// "incremental": true, /* Enable incremental compilation */
|
||||
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
|
||||
// "tsBuildInfoFile": "./", /* Specify the folder for .tsbuildinfo incremental compilation files. */
|
||||
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects */
|
||||
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
|
||||
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
|
||||
|
||||
/* Language and Environment */
|
||||
"target": "es2020", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
|
||||
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
|
||||
// "jsx": "preserve", /* Specify what JSX code is generated. */
|
||||
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
|
||||
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
|
||||
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */
|
||||
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
|
||||
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */
|
||||
// "reactNamespace": "", /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */
|
||||
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
|
||||
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
|
||||
|
||||
/* Modules */
|
||||
"module": "commonjs", /* Specify what module code is generated. */
|
||||
"rootDir": "./src", /* Specify the root folder within your source files. */
|
||||
// "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
|
||||
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
||||
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
|
||||
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
|
||||
// "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */
|
||||
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
|
||||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||
// "resolveJsonModule": true, /* Enable importing .json files */
|
||||
// "noResolve": true, /* Disallow `import`s, `require`s or `<reference>`s from expanding the number of files TypeScript should add to a project. */
|
||||
|
||||
/* JavaScript Support */
|
||||
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */
|
||||
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
|
||||
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */
|
||||
|
||||
/* Emit */
|
||||
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
|
||||
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
|
||||
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
|
||||
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
|
||||
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */
|
||||
"outDir": "./lib", /* Specify an output folder for all emitted files. */
|
||||
// "removeComments": true, /* Disable emitting comments. */
|
||||
// "noEmit": true, /* Disable emitting files from a compilation. */
|
||||
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
|
||||
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types */
|
||||
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
|
||||
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
|
||||
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
|
||||
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
|
||||
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
|
||||
// "newLine": "crlf", /* Set the newline character for emitting files. */
|
||||
// "stripInternal": true, /* Disable emitting declarations that have `@internal` in their JSDoc comments. */
|
||||
// "noEmitHelpers": true, /* Disable generating custom helper functions like `__extends` in compiled output. */
|
||||
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
|
||||
// "preserveConstEnums": true, /* Disable erasing `const enum` declarations in generated code. */
|
||||
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
|
||||
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
|
||||
|
||||
/* Interop Constraints */
|
||||
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
|
||||
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
|
||||
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */
|
||||
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
|
||||
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
|
||||
|
||||
/* Type Checking */
|
||||
"strict": true, /* Enable all strict type-checking options. */
|
||||
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied `any` type.. */
|
||||
// "strictNullChecks": true, /* When type checking, take into account `null` and `undefined`. */
|
||||
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
|
||||
// "strictBindCallApply": true, /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */
|
||||
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
|
||||
// "noImplicitThis": true, /* Enable error reporting when `this` is given the type `any`. */
|
||||
// "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */
|
||||
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
|
||||
// "noUnusedLocals": true, /* Enable error reporting when a local variables aren't read. */
|
||||
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read */
|
||||
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
|
||||
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
|
||||
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
|
||||
// "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */
|
||||
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
|
||||
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type */
|
||||
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
|
||||
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
|
||||
|
||||
/* Completeness */
|
||||
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
|
||||
"skipLibCheck": true /* Skip type checking all .d.ts files. */
|
||||
}
|
||||
}
|
|
@ -86,6 +86,22 @@ ReactDOM.render(
|
|||
)
|
||||
```
|
||||
|
||||
Using the icon font
|
||||
---
|
||||
If, for performance or other reasons, you wish to use the font implementation of these icons rather than the SVG implementation, you can specify `"fluentIcontFont"` as a condition for the import, either via [Node >= 12.19.0](https://nodejs.org/dist/latest-v16.x/docs/api/packages.html#resolving-user-conditions) or [webpack >= 5.0.0](https://webpack.js.org/configuration/resolve/#resolveconditionnames).
|
||||
|
||||
```js
|
||||
// webpack.config.js
|
||||
module.exports = {
|
||||
//...
|
||||
resolve: {
|
||||
conditionNames: ['fluentIcontFont', 'require', 'node'],
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
If you do choose this route, you may wish to use `@fluentui/react-icons-font-subsetting-webpack-plugin` to optimize the font assets.
|
||||
|
||||
Viewing the icons in a webpage
|
||||
---
|
||||
You can view the full list of available icons by type: [regular](https://github.com/microsoft/fluentui-system-icons/blob/master/icons_regular.md) or [filled](https://github.com/microsoft/fluentui-system-icons/blob/master/icons_filled.md)
|
||||
|
|
|
@ -0,0 +1,175 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
// @ts-check
|
||||
|
||||
const fs = require("fs/promises");
|
||||
const path = require("path");
|
||||
const process = require("process");
|
||||
const argv = require("yargs").boolean("selector").default("selector", false).argv;
|
||||
const _ = require("lodash");
|
||||
const mkdirp = require('mkdirp');
|
||||
const { promisify } = require('util');
|
||||
const glob = promisify(require('glob'));
|
||||
|
||||
// @ts-ignore
|
||||
const SRC_PATH = argv.source;
|
||||
// @ts-ignore
|
||||
const DEST_PATH = argv.dest;
|
||||
// @ts-ignore
|
||||
const CODEPOINT_DEST_PATH = argv.codepointDest;
|
||||
|
||||
if (!SRC_PATH) {
|
||||
throw new Error("Icon source folder not specified by --source");
|
||||
}
|
||||
if (!DEST_PATH) {
|
||||
throw new Error("Output destination folder not specified by --dest");
|
||||
}
|
||||
if (!CODEPOINT_DEST_PATH) {
|
||||
throw new Error("Output destination folder for codepoint map not specified by --dest");
|
||||
}
|
||||
|
||||
processFiles(SRC_PATH, DEST_PATH)
|
||||
|
||||
async function processFiles(src, dest) {
|
||||
/** @type string[] */
|
||||
const indexContents = [];
|
||||
|
||||
// make file for resizeable icons
|
||||
const iconPath = path.join(dest, 'icons')
|
||||
const iconContents = await processFolder(src, CODEPOINT_DEST_PATH, true);
|
||||
|
||||
await cleanFolder(iconPath);
|
||||
|
||||
await Promise.all(iconContents.map(async (chunk, i) => {
|
||||
const chunkFileName = `chunk-${i}`
|
||||
const chunkPath = path.resolve(iconPath, `${chunkFileName}.tsx`);
|
||||
indexContents.push(`export * from './icons/${chunkFileName}'`);
|
||||
await fs.writeFile(chunkPath, chunk);
|
||||
}));
|
||||
|
||||
// make file for sized icons
|
||||
const sizedIconPath = path.join(dest, 'sizedIcons');
|
||||
const sizedIconContents = await processFolder(src, CODEPOINT_DEST_PATH, false)
|
||||
await cleanFolder(sizedIconPath);
|
||||
|
||||
await Promise.all(sizedIconContents.map(async (chunk, i) => {
|
||||
const chunkFileName = `chunk-${i}`
|
||||
const chunkPath = path.resolve(sizedIconPath, `${chunkFileName}.tsx`);
|
||||
indexContents.push(`export * from './sizedIcons/${chunkFileName}'`);
|
||||
await fs.writeFile(chunkPath, chunk);
|
||||
}));
|
||||
|
||||
const indexPath = path.join(dest, 'index.tsx')
|
||||
// Finally add the interface definition and then write out the index.
|
||||
indexContents.push('export { FluentIconsProps } from \'../utils/FluentIconsProps.types\'');
|
||||
indexContents.push('export { default as wrapIcon } from \'../utils/wrapIcon\'');
|
||||
indexContents.push('export { default as bundleIcon } from \'../utils/bundleIcon\'');
|
||||
indexContents.push('export * from \'../utils/useIconState\'');
|
||||
indexContents.push('export * from \'../utils/constants\'');
|
||||
|
||||
await fs.writeFile(indexPath, indexContents.join('\n'));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a folder of svg files and convert them to React components, following naming patterns for the FluentUI System Icons
|
||||
* @param {string} srcPath
|
||||
* @param {string} codepointMapDestFolder
|
||||
* @param {boolean} resizable
|
||||
* @returns { Promise<string[]> } - chunked icon files to insert
|
||||
*/
|
||||
async function processFolder(srcPath, codepointMapDestFolder, resizable) {
|
||||
var files = await glob(resizable ? 'FluentSystemIcons-Resizable.json' : 'FluentSystemIcons-{Filled,Regular}.json', { cwd: srcPath, absolute: true });
|
||||
|
||||
/** @type string[] */
|
||||
const iconExports = [];
|
||||
await Promise.all(files.map(async (srcFile, index) => {
|
||||
/** @type {Record<string, number>} */
|
||||
const iconEntries = JSON.parse(await fs.readFile(srcFile, 'utf8'));
|
||||
iconExports.push(...generateReactIconEntries(iconEntries, resizable));
|
||||
|
||||
return generateCodepointMapForWebpackPlugin(
|
||||
path.resolve(codepointMapDestFolder, path.basename(srcFile)),
|
||||
iconEntries,
|
||||
resizable
|
||||
);
|
||||
}));
|
||||
|
||||
// chunk all icons into separate files to keep build reasonably fast
|
||||
/** @type string[][] */
|
||||
const iconChunks = [];
|
||||
while (iconExports.length > 0) {
|
||||
iconChunks.push(iconExports.splice(0, 500));
|
||||
}
|
||||
|
||||
for (const chunk of iconChunks) {
|
||||
chunk.unshift(`import {createFluentFontIcon} from "../../utils/fonts/createFluentFontIcon";`)
|
||||
}
|
||||
|
||||
/** @type string[] */
|
||||
const chunkContent = iconChunks.map(chunk => chunk.join('\n'));
|
||||
|
||||
return chunkContent;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} destPath
|
||||
* @param {Record<string,number>} iconEntries
|
||||
* @param {boolean} resizable
|
||||
*/
|
||||
async function generateCodepointMapForWebpackPlugin(destPath, iconEntries, resizable) {
|
||||
const finalCodepointMap = Object.fromEntries(
|
||||
Object.entries(iconEntries)
|
||||
.map(([name, codepoint]) => [getReactIconNameFromGlyphName(name, resizable), codepoint])
|
||||
);
|
||||
|
||||
await fs.writeFile(destPath, JSON.stringify(finalCodepointMap, null, 2));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Record<string, number>} iconEntries
|
||||
* @param {boolean} resizable
|
||||
* @returns {string[]}
|
||||
*/
|
||||
function generateReactIconEntries(iconEntries, resizable) {
|
||||
/** @type {string[]} */
|
||||
const iconExports = [];
|
||||
for (const [iconName, codepoint] of Object.entries(iconEntries)) {
|
||||
let destFilename = getReactIconNameFromGlyphName(iconName, resizable);
|
||||
|
||||
var jsCode = `export const ${destFilename} = /*#__PURE__*/createFluentFontIcon(${JSON.stringify(destFilename)
|
||||
}, ${JSON.stringify(String.fromCodePoint(codepoint))
|
||||
}, ${resizable ? 2 /* Resizable */ : /filled$/i.test(iconName) ? 0 /* Filled */ : 1 /* Regular */
|
||||
}${resizable ? '' : `, ${/(?<=_)\d+(?=_filled|_regular)/.exec(iconName)[0]}`
|
||||
});`;
|
||||
|
||||
iconExports.push(jsCode);
|
||||
}
|
||||
|
||||
return iconExports;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} iconName
|
||||
* @param {boolean} resizable
|
||||
* @returns {string}
|
||||
*/
|
||||
function getReactIconNameFromGlyphName(iconName, resizable) {
|
||||
let destFilename = iconName.replace("ic_fluent_", ""); // strip ic_fluent_
|
||||
destFilename = resizable ? destFilename.replace("20", "") : destFilename;
|
||||
destFilename = _.camelCase(destFilename); // We want them to be camelCase, so access_time would become accessTime here
|
||||
destFilename = destFilename.replace(destFilename.substring(0, 1), destFilename.substring(0, 1).toUpperCase()); // capitalize the first letter
|
||||
return destFilename;
|
||||
}
|
||||
|
||||
async function cleanFolder(folder) {
|
||||
try {
|
||||
await fs.access(folder);
|
||||
await fs.rm(folder, { recursive: true, force: true });
|
||||
} catch { }
|
||||
|
||||
await mkdirp(folder);
|
||||
}
|
|
@ -82,10 +82,10 @@ function processFiles(src, dest) {
|
|||
/**
|
||||
* Process a folder of svg files and convert them to React components, following naming patterns for the FluentUI System Icons
|
||||
* @param {string} srcPath
|
||||
* @param {boolean} oneSize
|
||||
* @param {boolean} resizable
|
||||
* @returns { string [] } - chunked icon files to insert
|
||||
*/
|
||||
function processFolder(srcPath, destPath, oneSize) {
|
||||
function processFolder(srcPath, destPath, resizable) {
|
||||
var files = fs.readdirSync(srcPath)
|
||||
|
||||
// These options will be passed to svgr/core
|
||||
|
@ -120,18 +120,18 @@ function processFolder(srcPath, destPath, oneSize) {
|
|||
// }
|
||||
// indexContents += processFolder(srcFile, joinedDestPath)
|
||||
} else {
|
||||
if(oneSize && !file.includes("20")) {
|
||||
if(resizable && !file.includes("20")) {
|
||||
return
|
||||
}
|
||||
var iconName = file.substr(0, file.length - 4) // strip '.svg'
|
||||
iconName = iconName.replace("ic_fluent_", "") // strip ic_fluent_
|
||||
iconName = oneSize ? iconName.replace("20", "") : iconName
|
||||
iconName = resizable ? iconName.replace("20", "") : iconName
|
||||
var destFilename = _.camelCase(iconName) // We want them to be camelCase, so access_time would become accessTime here
|
||||
destFilename = destFilename.replace(destFilename.substring(0, 1), destFilename.substring(0, 1).toUpperCase()) // capitalize the first letter
|
||||
|
||||
var iconContent = fs.readFileSync(srcFile, { encoding: "utf8" })
|
||||
|
||||
var jsxCode = oneSize ? svgr.default.sync(iconContent, svgrOpts, { filePath: file }) : svgr.default.sync(iconContent, svgrOptsSizedIcons, { filePath: file })
|
||||
var jsxCode = resizable ? svgr.default.sync(iconContent, svgrOpts, { filePath: file }) : svgr.default.sync(iconContent, svgrOptsSizedIcons, { filePath: file })
|
||||
var jsCode =
|
||||
`
|
||||
const ${destFilename}Icon = (iconProps: FluentIconsProps) => {
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -15,23 +15,32 @@
|
|||
"clean": "find ./src -type f ! -name \"wrapIcon.tsx\" -name \"*.tsx\" -delete",
|
||||
"cleanSvg": "rm -rf ./intermediate",
|
||||
"copy": "node ../../importer/generate.js --source=../../assets --dest=./intermediate --extension=svg --target=react",
|
||||
"convert": "node convert.js --source=./intermediate --dest=./src",
|
||||
"copy:font-files": "cpy './src/utils/fonts/*.{ttf,woff,woff2,json}' ./lib/utils/fonts/. && cpy './src/utils/fonts/*.{ttf,woff,woff2,json}' ./lib-cjs/utils/fonts/.",
|
||||
"convert:svg": "node convert.js --source=./intermediate --dest=./src",
|
||||
"convert:fonts": "node convert-font.js --source=./src/utils/fonts --dest=./src/fonts --codepointDest=./src/utils/fonts",
|
||||
"generate:font-regular": "node ../../importer/generateFont.js --source=intermediate --dest=src/utils/fonts --iconType=Regular --codepoints=../../fonts/FluentSystemIcons-Regular.json",
|
||||
"generate:font-filled": "node ../../importer/generateFont.js --source=intermediate --dest=src/utils/fonts --iconType=Filled --codepoints=../../fonts/FluentSystemIcons-Filled.json",
|
||||
"generate:font-resizable": "node ../../importer/generateFont.js --source=intermediate --dest=src/utils/fonts --iconType=Resizable",
|
||||
"generate:font": "npm run generate:font-regular && npm run generate:font-filled && npm run generate:font-resizable",
|
||||
"rollup": "node ./generateRollup.js",
|
||||
"optimize": "svgo --folder=./intermediate --precision=2 --disable=removeViewBox,mergePaths",
|
||||
"unfill": "find ./intermediate -type f -name \"*.svg\" -exec sed -i.bak 's/fill=\"none\"//g' {} \\; && find ./intermediate -type f -name \"*.bak\" -delete",
|
||||
"build": "npm run copy && npm run optimize && npm run unfill && npm run convert && npm run cleanSvg && npm run build:esm && npm run build:cjs",
|
||||
"build": "npm run copy && npm run generate:font && npm run optimize && npm run unfill && npm run convert:svg && npm run convert:fonts && npm run cleanSvg && npm run build:esm && npm run build:cjs && npm run copy:font-files",
|
||||
"build:cjs": "tsc --module commonjs --outDir lib-cjs && babel lib-cjs --out-dir lib-cjs",
|
||||
"build:esm": "tsc && babel lib --out-dir lib"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.16.0",
|
||||
"@babel/core": "^7.16.0",
|
||||
"@babel/preset-typescript": "^7.16.7",
|
||||
"@griffel/babel-preset": "^1.0.0",
|
||||
"@griffel/react": "^1.0.0",
|
||||
"@svgr/core": "^5.5.0",
|
||||
"@types/react": "^17.0.2",
|
||||
"glob": "^7.1.7",
|
||||
"cpy-cli": "^4.1.0",
|
||||
"glob": "^7.2.0",
|
||||
"lodash": "^4.17.21",
|
||||
"mkdirp": "^1.0.4",
|
||||
"react": "^17.0.1",
|
||||
"renamer": "^2.0.1",
|
||||
"svgo": "^1.3.2",
|
||||
|
@ -46,5 +55,29 @@
|
|||
"files": [
|
||||
"lib/",
|
||||
"lib-cjs/"
|
||||
]
|
||||
}
|
||||
],
|
||||
"exports": {
|
||||
".": {
|
||||
"fluentIcontFont": {
|
||||
"types": "./lib/fonts/index.d.ts",
|
||||
"import": "./lib/fonts/index.js",
|
||||
"require": "./lib-cjs/fonts/index.js"
|
||||
},
|
||||
"default": {
|
||||
"types": "./lib/index.d.ts",
|
||||
"import": "./lib/index.js",
|
||||
"require": "./lib-cjs/index.js"
|
||||
}
|
||||
},
|
||||
"./lib/fonts": {
|
||||
"types": "./lib/fonts/index.d.ts",
|
||||
"import": "./lib/fonts/index.js",
|
||||
"require": "./lib-cjs/fonts/index.js"
|
||||
},
|
||||
"./lib/svg": {
|
||||
"types": "./lib/index.d.ts",
|
||||
"import": "./lib/index.js",
|
||||
"require": "./lib-cjs/index.js"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
fonts/
|
|
@ -1,6 +1,6 @@
|
|||
import * as React from "react";
|
||||
|
||||
export interface FluentIconsProps extends React.SVGAttributes<SVGElement> {
|
||||
export type FluentIconsProps<TBaseAttributes extends (React.SVGAttributes<SVGElement> | React.HTMLAttributes<HTMLElement>) = React.SVGAttributes<SVGElement>> = (TBaseAttributes) & {
|
||||
primaryFill?: string
|
||||
className?: string
|
||||
filled?: boolean
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
FluentSystemIcons*
|
|
@ -0,0 +1,99 @@
|
|||
import * as React from 'react';
|
||||
import { FluentIconsProps } from '../FluentIconsProps.types';
|
||||
import { makeStyles, makeStaticStyles, mergeClasses } from "@griffel/react";
|
||||
import { useIconState } from '../useIconState';
|
||||
|
||||
import fontFilledTtf from './FluentSystemIcons-Filled.ttf';
|
||||
import fontFilledWoff from './FluentSystemIcons-Filled.woff';
|
||||
import fontFilledWoff2 from './FluentSystemIcons-Filled.woff2';
|
||||
|
||||
import fontRegularTtf from './FluentSystemIcons-Regular.ttf';
|
||||
import fontRegularWoff from './FluentSystemIcons-Regular.woff';
|
||||
import fontRegularWoff2 from './FluentSystemIcons-Regular.woff2';
|
||||
|
||||
import fontOneSizeTtf from './FluentSystemIcons-Resizable.ttf';
|
||||
import fontOneSizeWoff from './FluentSystemIcons-Resizable.woff';
|
||||
import fontOneSizeWoff2 from './FluentSystemIcons-Resizable.woff2';
|
||||
|
||||
export const enum FontFile {
|
||||
Filled = 0,
|
||||
Regular = 1,
|
||||
Resizable = 2
|
||||
}
|
||||
|
||||
const FONT_FAMILY_MAP = {
|
||||
[FontFile.Filled]: 'FluentSystemIconsFilled',
|
||||
[FontFile.Regular]: 'FluentSystemIconsRegular',
|
||||
[FontFile.Resizable]: 'FluentSystemIcons',
|
||||
} as const;
|
||||
|
||||
const useStaticStyles = makeStaticStyles(`
|
||||
@font-face {
|
||||
font-family: ${FONT_FAMILY_MAP[FontFile.Filled]};
|
||||
src: url(${JSON.stringify(fontFilledWoff2)}) format("woff2"),
|
||||
url(${JSON.stringify(fontFilledWoff)}) format("woff"),
|
||||
url(${JSON.stringify(fontFilledTtf)}) format("truetype");
|
||||
}
|
||||
@font-face {
|
||||
font-family: ${FONT_FAMILY_MAP[FontFile.Regular]};
|
||||
src: url(${JSON.stringify(fontRegularWoff2)}) format("woff2"),
|
||||
url(${JSON.stringify(fontRegularWoff)}) format("woff"),
|
||||
url(${JSON.stringify(fontRegularTtf)}) format("truetype");
|
||||
}
|
||||
@font-face {
|
||||
font-family: ${FONT_FAMILY_MAP[FontFile.Resizable]};
|
||||
src: url(${JSON.stringify(fontOneSizeWoff2)}) format("woff2"),
|
||||
url(${JSON.stringify(fontOneSizeWoff)}) format("woff"),
|
||||
url(${JSON.stringify(fontOneSizeTtf)}) format("truetype");
|
||||
}
|
||||
`)
|
||||
|
||||
const useRootStyles = makeStyles({
|
||||
root: {
|
||||
display: 'inline-block',
|
||||
fontStyle: 'normal',
|
||||
lineHeight: '1em',
|
||||
|
||||
"@media (forced-colors: active)": {
|
||||
forcedColorAdjust: 'auto',
|
||||
}
|
||||
},
|
||||
[FontFile.Filled]: {
|
||||
fontFamily: 'FluentSystemIconsFilled',
|
||||
},
|
||||
[FontFile.Regular]: {
|
||||
fontFamily: 'FluentSystemIconsRegular',
|
||||
},
|
||||
[FontFile.Resizable]: {
|
||||
fontFamily: 'FluentSystemIcons',
|
||||
},
|
||||
});
|
||||
|
||||
export function createFluentFontIcon(displayName: string, codepoint: string, font: FontFile, fontSize?: number): React.FC<FluentIconsProps<React.HTMLAttributes<HTMLElement>>> {
|
||||
const Component: React.FC<FluentIconsProps<React.HTMLAttributes<HTMLElement>>> = (props) => {
|
||||
useStaticStyles();
|
||||
const styles = useRootStyles();
|
||||
props.className = mergeClasses(styles.root, styles[font], props.className);
|
||||
const { primaryFill, ...state } = useIconState<React.HTMLAttributes<HTMLElement>>(props);
|
||||
|
||||
|
||||
// We want to keep the same API surface as the SVG icons, so translate `primaryFill` to `color`
|
||||
if (primaryFill) {
|
||||
state.style = {
|
||||
...state.style,
|
||||
color: primaryFill
|
||||
}
|
||||
}
|
||||
|
||||
if (fontSize) {
|
||||
state.style = {
|
||||
...state.style,
|
||||
fontSize
|
||||
}
|
||||
}
|
||||
|
||||
return <i {...state}>{codepoint}</i>
|
||||
}
|
||||
Component.displayName = displayName;
|
||||
return Component;
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
declare module '*.ttf' {
|
||||
const uri: string;
|
||||
export default uri;
|
||||
}
|
||||
|
||||
declare module '*.woff' {
|
||||
const uri: string;
|
||||
export default uri;
|
||||
}
|
||||
|
||||
declare module '*.woff2' {
|
||||
const uri: string;
|
||||
export default uri;
|
||||
}
|
|
@ -12,7 +12,7 @@ const useRootStyles = makeStyles({
|
|||
}
|
||||
});
|
||||
|
||||
export const useIconState = (props: FluentIconsProps): FluentIconsProps => {
|
||||
export const useIconState = <TBaseAttributes extends (React.SVGAttributes<SVGElement> | React.HTMLAttributes<HTMLElement>) = React.SVGAttributes<SVGElement>>(props: FluentIconsProps<TBaseAttributes>): FluentIconsProps<TBaseAttributes> => {
|
||||
const { title, primaryFill = "currentColor", ...rest } = props;
|
||||
const state = {
|
||||
...rest,
|
||||
|
@ -34,5 +34,5 @@ export const useIconState = (props: FluentIconsProps): FluentIconsProps => {
|
|||
state['role'] = 'img';
|
||||
}
|
||||
|
||||
return state;
|
||||
return state as unknown as FluentIconsProps<TBaseAttributes>;
|
||||
};
|
||||
|
|
|
@ -10,7 +10,9 @@
|
|||
"forceConsistentCasingInFileNames": true,
|
||||
"strictNullChecks": true,
|
||||
"moduleResolution": "node",
|
||||
"jsx": "react"
|
||||
"jsx": "react",
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true
|
||||
},
|
||||
"include": ["src"],
|
||||
"exclude": ["lib", "lib-cjs"]
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Загрузка…
Ссылка в новой задаче