diff --git a/.fatherrc.js b/.fatherrc.js
index fa714010..fcb12e40 100644
--- a/.fatherrc.js
+++ b/.fatherrc.js
@@ -13,6 +13,7 @@ const headPkgs = [
"fes-plugin-access",
"fes-plugin-model",
"fes-plugin-layout",
+ "fes-plugin-icon",
];
const tailPkgs = [];
// const otherPkgs = readdirSync(join(__dirname, 'packages')).filter(
diff --git a/packages/fes-core/src/service/pluginAPI.js b/packages/fes-core/src/service/pluginAPI.js
index 64e2efd4..d4d67511 100644
--- a/packages/fes-core/src/service/pluginAPI.js
+++ b/packages/fes-core/src/service/pluginAPI.js
@@ -28,7 +28,7 @@ export default class PluginAPI {
if (plugins[id]) {
const name = plugins[id].isPreset ? 'preset' : 'plugin';
throw new Error(
- `api.describe() failed, ${name} ${id} is already registered by ${plugins[id].path}.`,
+ `api.describe() failed, ${name} ${id} is already registered by ${plugins[id].path}.`
);
}
plugins[id] = plugins[this.id];
@@ -51,11 +51,11 @@ export default class PluginAPI {
register(hook) {
assert(
hook.key && typeof hook.key === 'string',
- `api.register() failed, hook.key must supplied and should be string, but got ${hook.key}.`,
+ `api.register() failed, hook.key must supplied and should be string, but got ${hook.key}.`
);
assert(
hook.fn && typeof hook.fn === 'function',
- `api.register() failed, hook.fn must supplied and should be function, but got ${hook.fn}.`,
+ `api.register() failed, hook.fn must supplied and should be function, but got ${hook.fn}.`
);
this.service.hooksByPluginId[this.id] = (
this.service.hooksByPluginId[this.id] || []
@@ -66,7 +66,7 @@ export default class PluginAPI {
const { name, alias } = command;
assert(
!this.service.commands[name],
- `api.registerCommand() failed, the command ${name} is exists.`,
+ `api.registerCommand() failed, the command ${name} is exists.`
);
this.service.commands[name] = command;
if (alias) {
@@ -79,11 +79,11 @@ export default class PluginAPI {
assert(
this.service.stage === ServiceStage.initPresets
|| this.service.stage === ServiceStage.initPlugins,
- 'api.registerPlugins() failed, it should only be used in registering stage.',
+ 'api.registerPlugins() failed, it should only be used in registering stage.'
);
assert(
Array.isArray(plugins),
- 'api.registerPlugins() failed, plugins must be Array.',
+ 'api.registerPlugins() failed, plugins must be Array.'
);
const extraPlugins = plugins.map(plugin => (isValidPlugin(plugin)
? (plugin)
@@ -102,11 +102,11 @@ export default class PluginAPI {
registerPresets(presets) {
assert(
this.service.stage === ServiceStage.initPresets,
- 'api.registerPresets() failed, it should only used in presets.',
+ 'api.registerPresets() failed, it should only used in presets.'
);
assert(
Array.isArray(presets),
- 'api.registerPresets() failed, presets must be Array.',
+ 'api.registerPresets() failed, presets must be Array.'
);
const extraPresets = presets.map(preset => (isValidPlugin(preset)
? (preset)
@@ -127,7 +127,7 @@ export default class PluginAPI {
if (this.service.pluginMethods[name]) {
if (exitsError) {
throw new Error(
- `api.registerMethod() failed, method ${name} is already exist.`,
+ `api.registerMethod() failed, method ${name} is already exist.`
);
} else {
return;
diff --git a/packages/fes-plugin-icon/.fatherrc.js b/packages/fes-plugin-icon/.fatherrc.js
new file mode 100644
index 00000000..332f1bff
--- /dev/null
+++ b/packages/fes-plugin-icon/.fatherrc.js
@@ -0,0 +1,3 @@
+export default {
+ disableTypeCheck: false,
+};
diff --git a/packages/fes-plugin-icon/package.json b/packages/fes-plugin-icon/package.json
new file mode 100644
index 00000000..4a8c204b
--- /dev/null
+++ b/packages/fes-plugin-icon/package.json
@@ -0,0 +1,22 @@
+{
+ "name": "@webank/fes-plugin-icon",
+ "version": "1.0.0",
+ "description": "",
+ "main": "lib/index.js",
+ "files": [
+ "lib"
+ ],
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "module": "dist/index.esm.js",
+ "keywords": [],
+ "author": "",
+ "license": "MIT",
+ "peerDependencies": {
+ "vue": "^3.0.4"
+ },
+ "dependencies": {
+ "svgo": "1.3.2"
+ }
+}
diff --git a/packages/fes-plugin-icon/src/Icon/Icon.jsx b/packages/fes-plugin-icon/src/Icon/Icon.jsx
new file mode 100644
index 00000000..beccaa64
--- /dev/null
+++ b/packages/fes-plugin-icon/src/Icon/Icon.jsx
@@ -0,0 +1,39 @@
+import { computed } from 'vue';
+// eslint-disable-next-line
+import icons from './icons';
+
+const noop = () => { };
+
+export default {
+ name: 'FesIcon',
+ props: ['type', 'spin', 'rotate', 'tabIndex'],
+ setup(props, { attrs }) {
+ const CurrentIcon = computed(() => icons[props.type]);
+ const iconTabIndex = computed(() => {
+ let tabIndex = props.tabIndex;
+ if (tabIndex == null && attrs.onClick) {
+ tabIndex = -1;
+ }
+ return tabIndex;
+ });
+ const svgStyle = computed(() => (props.rotate
+ ? {
+ msTransform: `rotate(${props.rotate}deg)`,
+ transform: `rotate(${props.rotate}deg)`
+ }
+ : null));
+ const svgCls = computed(() => ({
+ 'inner-icon--spin': !!props.spin || props.type === 'loading'
+ }));
+ return () => (
+
+
+
+ );
+ }
+};
diff --git a/packages/fes-template-h5/src/components/Icon/icon.less b/packages/fes-plugin-icon/src/Icon/icon.less
similarity index 91%
rename from packages/fes-template-h5/src/components/Icon/icon.less
rename to packages/fes-plugin-icon/src/Icon/icon.less
index 260abebd..e2efbaaa 100644
--- a/packages/fes-template-h5/src/components/Icon/icon.less
+++ b/packages/fes-plugin-icon/src/Icon/icon.less
@@ -1,8 +1,6 @@
-@import "~@/styles/theme.less";
-
.inner-icon {
display: inline-block;
- color: @icon-color;
+ color: inherit;
font-style: normal;
line-height: 0;
text-align: center;
@@ -19,6 +17,9 @@
svg {
display: inline-block;
+ width: 1em;
+ height: 1em;
+ fill: currentColor;
}
&::before {
diff --git a/packages/fes-template-h5/src/components/Icon/index.js b/packages/fes-plugin-icon/src/Icon/index.js
similarity index 100%
rename from packages/fes-template-h5/src/components/Icon/index.js
rename to packages/fes-plugin-icon/src/Icon/index.js
diff --git a/packages/fes-plugin-icon/src/icons.tpl b/packages/fes-plugin-icon/src/icons.tpl
new file mode 100644
index 00000000..9bb58afb
--- /dev/null
+++ b/packages/fes-plugin-icon/src/icons.tpl
@@ -0,0 +1,9 @@
+{{#ICON_NAMES}}
+import smile from './icons/{{.}}';
+{{/ICON_NAMES}}
+
+export default {
+ {{#ICON_NAMES}}
+ {{.}}
+ {{/ICON_NAMES}}
+};
\ No newline at end of file
diff --git a/packages/fes-plugin-icon/src/index.js b/packages/fes-plugin-icon/src/index.js
new file mode 100644
index 00000000..9b7819ce
--- /dev/null
+++ b/packages/fes-plugin-icon/src/index.js
@@ -0,0 +1,76 @@
+import { readFileSync, copyFileSync, statSync } from 'fs';
+import { join, basename } from 'path';
+import optimizeSvg from './optimizeSvg';
+
+export default (api) => {
+ api.addRuntimePluginKey(() => '');
+ // 配置
+ api.describe({
+ key: 'icon',
+ config: {
+ schema(joi) {
+ return joi.object();
+ }
+ }
+ });
+
+ const namespace = 'plugin-icon';
+ const absRuntimeFilePath = join(namespace, 'runtime.js');
+
+ // TODO 监听 icons 文件变更,重新生成文件
+ api.onGenerateFiles(async () => {
+ const base = join(api.paths.absSrcPath, 'icons');
+ const iconFiles = api.utils.glob.sync('**/*', {
+ cwd: join(api.paths.absSrcPath, 'icons')
+ });
+ const svgDatas = await optimizeSvg(iconFiles.map(item => join(base, item)));
+ const iconNames = [];
+ const SVG_COMPONENT_TMPLATE = 'export default () => (SVG)';
+ for (const { fileName, data } of svgDatas) {
+ iconNames.push(basename(fileName, '.svg'));
+ api.writeTmpFile({
+ path: `${namespace}/icons/${basename(fileName, '.svg')}.js`,
+ content: SVG_COMPONENT_TMPLATE
+ .replace('SVG', data)
+ });
+ }
+
+ api.writeTmpFile({
+ path: `${namespace}/icons.js`,
+ content: api.utils.Mustache.render(
+ readFileSync(join(__dirname, './icons.tpl'), 'utf-8'),
+ {
+ ICON_NAMES: iconNames
+ }
+ )
+ });
+
+ api.writeTmpFile({
+ path: absRuntimeFilePath,
+ content: api.utils.Mustache.render(readFileSync(join(__dirname, 'runtime.tpl'), 'utf-8'), {
+ })
+ });
+ });
+
+ let generatedOnce = false;
+ api.onGenerateFiles(() => {
+ if (generatedOnce) return;
+ generatedOnce = true;
+ const cwd = join(__dirname, '../src/Icon');
+ const files = api.utils.glob.sync('**/*', {
+ cwd
+ });
+ const base = join(api.paths.absTmpPath, namespace);
+ files.forEach((file) => {
+ const source = join(cwd, file);
+ const target = join(base, file);
+ if (statSync(source).isDirectory()) {
+ api.utils.mkdirp.sync(target);
+ } else {
+ copyFileSync(source, target);
+ }
+ });
+ });
+
+ api.addRuntimePlugin(() => `@@/${absRuntimeFilePath}`);
+};
diff --git a/packages/fes-plugin-icon/src/optimizeSvg.js b/packages/fes-plugin-icon/src/optimizeSvg.js
new file mode 100644
index 00000000..f5c2886b
--- /dev/null
+++ b/packages/fes-plugin-icon/src/optimizeSvg.js
@@ -0,0 +1,91 @@
+import { extname, basename } from 'path';
+import { statSync, readFileSync } from 'fs';
+
+
+import SVGO from 'svgo/lib/svgo';
+
+const svgo = new SVGO({
+ plugins: [{
+ cleanupAttrs: true
+ }, {
+ removeDoctype: true
+ }, {
+ removeXMLProcInst: true
+ }, {
+ removeComments: true
+ }, {
+ removeMetadata: true
+ }, {
+ removeTitle: true
+ }, {
+ removeDesc: true
+ }, {
+ removeUselessDefs: true
+ }, {
+ removeEditorsNSData: true
+ }, {
+ removeEmptyAttrs: true
+ }, {
+ removeHiddenElems: true
+ }, {
+ removeEmptyText: true
+ }, {
+ removeEmptyContainers: true
+ }, {
+ removeViewBox: false
+ }, {
+ cleanupEnableBackground: true
+ }, {
+ convertStyleToAttrs: true
+ }, {
+ convertColors: true
+ }, {
+ convertPathData: true
+ }, {
+ convertTransform: true
+ }, {
+ removeUnknownsAndDefaults: true
+ }, {
+ removeNonInheritableGroupAttrs: true
+ }, {
+ removeUselessStrokeAndFill: true
+ }, {
+ removeUnusedNS: true
+ }, {
+ cleanupIDs: true
+ }, {
+ cleanupNumericValues: true
+ }, {
+ moveElemsAttrsToGroup: true
+ }, {
+ moveGroupAttrsToElems: true
+ }, {
+ collapseGroups: true
+ }, {
+ removeRasterImages: false
+ }, {
+ mergePaths: true
+ }, {
+ convertShapeToPath: true
+ }, {
+ sortAttrs: true
+ }, {
+ removeDimensions: true
+ }, {
+ removeAttrs: { attrs: '(stroke|fill)' }
+ }]
+});
+
+export default function optimizeSvg(files) {
+ const optimizedSvgData = [];
+ for (const filePath of files) {
+ if (statSync(filePath).isFile() && extname(filePath) === '.svg') {
+ const data = readFileSync(filePath, 'utf-8');
+ optimizedSvgData.push(svgo.optimize(data, { path: filePath }).then(svgData => ({
+ fileName: basename(filePath),
+ ...svgData
+ })));
+ }
+ }
+ return Promise.all(optimizedSvgData);
+}
diff --git a/packages/fes-plugin-icon/src/runtime.tpl b/packages/fes-plugin-icon/src/runtime.tpl
new file mode 100644
index 00000000..ecedb2b7
--- /dev/null
+++ b/packages/fes-plugin-icon/src/runtime.tpl
@@ -0,0 +1,5 @@
+import Icon from './index';
+
+export function onAppCreated({ app }) {
+ app.component('fes-icon', Icon);
+}
diff --git a/packages/fes-preset-built-in/src/plugins/features/devScripts.js b/packages/fes-preset-built-in/src/plugins/features/devScripts.js
index 954d2ead..7260ab4e 100644
--- a/packages/fes-preset-built-in/src/plugins/features/devScripts.js
+++ b/packages/fes-preset-built-in/src/plugins/features/devScripts.js
@@ -46,7 +46,7 @@ export default (api) => {
});
api.addHTMLHeadScripts(() => [{
- src: `${api.config.publicPath}@@/devScripts.js`
+ src: '@@/devScripts.js'
}]);
api.onGenerateFiles(() => {
diff --git a/packages/fes-preset-built-in/src/plugins/features/html.js b/packages/fes-preset-built-in/src/plugins/features/html.js
index 5dc7590e..3a5d4924 100644
--- a/packages/fes-preset-built-in/src/plugins/features/html.js
+++ b/packages/fes-preset-built-in/src/plugins/features/html.js
@@ -112,7 +112,7 @@ export default (api) => {
webpackConfig
.plugin('html-tags')
.use(HtmlWebpackTagsPlugin, [{
- append: true,
+ append: false,
scripts: headScripts.map(script => ({
path: script.src
}))
diff --git a/packages/fes-template-h5/package.json b/packages/fes-template-h5/package.json
index 16594710..c6fc0b1f 100644
--- a/packages/fes-template-h5/package.json
+++ b/packages/fes-template-h5/package.json
@@ -34,6 +34,7 @@
},
"dependencies": {
"vue": "^3.0.4",
- "@webank/fes": "^2.0.0"
+ "@webank/fes": "^2.0.0",
+ "@webank/fes-plugin-icon": "^1.0.0"
}
}
diff --git a/packages/fes-template-h5/src/components/Icon/Icon.vue b/packages/fes-template-h5/src/components/Icon/Icon.vue
deleted file mode 100644
index edce5d17..00000000
--- a/packages/fes-template-h5/src/components/Icon/Icon.vue
+++ /dev/null
@@ -1,50 +0,0 @@
-
-
-
-
-
-
-
diff --git a/packages/fes-template-h5/src/components/Icon/IconBase.js b/packages/fes-template-h5/src/components/Icon/IconBase.js
deleted file mode 100644
index 8ee7d187..00000000
--- a/packages/fes-template-h5/src/components/Icon/IconBase.js
+++ /dev/null
@@ -1,36 +0,0 @@
-import { generate } from './util';
-
-const IconBase = {
- functional: true,
- props: ['icon'],
- render(h, ctx) {
- const {
- data: { attrs, ...restData } = {},
- props = {},
- listeners
- } = ctx;
- const { icon, ...restProps } = {
- ...attrs,
- ...props
- };
- if (!icon) return null;
- const target = icon;
-
- return generate(h, target.icon, `svg-${target.name}`, {
- ...restData,
- attrs: {
- 'data-icon': target.name,
- width: '1em',
- height: '1em',
- fill: 'currentColor',
- 'aria-hidden': 'true',
- ...restProps
- },
- on: listeners
- });
- }
-};
-
-IconBase.name = 'IconBase';
-
-export default IconBase;
diff --git a/packages/fes-template-h5/src/components/Icon/icons.js b/packages/fes-template-h5/src/components/Icon/icons.js
deleted file mode 100644
index b69602e4..00000000
--- a/packages/fes-template-h5/src/components/Icon/icons.js
+++ /dev/null
@@ -1,8 +0,0 @@
-/* Automatically generated by './build/bin/gen-icons.js' */
-
-import smile from './icons/smile.svg';
-
-
-export default {
- smile
-};
diff --git a/packages/fes-template-h5/src/components/Icon/util.js b/packages/fes-template-h5/src/components/Icon/util.js
deleted file mode 100644
index f57f95bd..00000000
--- a/packages/fes-template-h5/src/components/Icon/util.js
+++ /dev/null
@@ -1,33 +0,0 @@
-export function normalizeAttrs(attrs = {}) {
- return Object.keys(attrs).reduce((acc, key) => {
- const val = attrs[key];
- switch (key) {
- case 'class':
- acc.className = val;
- delete acc.class;
- break;
- default:
- acc[key] = val;
- }
- return acc;
- }, {});
-}
-
-export function generate(h, node, key, rootProps) {
- if (!rootProps) {
- return h(
- node.tag,
- { key, attrs: { ...normalizeAttrs(node.attrs) } },
- (node.children || []).map((child, index) => generate(h, child, `${key}-${node.tag}-${index}`))
- );
- }
- return h(
- node.tag,
- {
- key,
- ...rootProps,
- attrs: { ...normalizeAttrs(node.attrs), ...rootProps.attrs }
- },
- (node.children || []).map((child, index) => generate(h, child, `${key}-${node.tag}-${index}`))
- );
-}
diff --git a/packages/fes-template-h5/src/components/Icon/icons/smile.svg b/packages/fes-template-h5/src/icons/smile.svg
similarity index 100%
rename from packages/fes-template-h5/src/components/Icon/icons/smile.svg
rename to packages/fes-template-h5/src/icons/smile.svg
diff --git a/packages/fes-template-h5/src/pages/index.vue b/packages/fes-template-h5/src/pages/index.vue
index 59343bbf..60c4add9 100644
--- a/packages/fes-template-h5/src/pages/index.vue
+++ b/packages/fes-template-h5/src/pages/index.vue
@@ -1,7 +1,7 @@
fes & 拉夫德鲁
-
+
@@ -18,6 +18,7 @@ import { useRouter } from '@webank/fes';
export default {
setup() {
const fes = ref('fes upgrade to vue3');
+ const rotate = ref(90);
const router = useRouter();
onMounted(() => {
console.log(router);
@@ -27,8 +28,13 @@ export default {
onMounted(() => {
console.log('mounted2!!');
});
+ const clickIcon = () => {
+ console.log('click Icon');
+ };
return {
- fes
+ fes,
+ rotate,
+ clickIcon
};
}
};
@@ -36,6 +42,7 @@ export default {
diff --git a/packages/fes-template-h5/src/styles/mixins/hairline.less b/packages/fes-template-h5/src/styles/mixins/hairline.less
index f0bb934d..c95c6f3d 100644
--- a/packages/fes-template-h5/src/styles/mixins/hairline.less
+++ b/packages/fes-template-h5/src/styles/mixins/hairline.less
@@ -18,6 +18,7 @@
html:not([data-scale]) & {
@media (min-resolution: 2dppx) {
border-top: none;
+ position: relative;
&::before {
.scale-hairline-common(@color, 0, auto, auto, 0);
@@ -40,6 +41,7 @@
html:not([data-scale]) & {
@media (min-resolution: 2dppx) {
border-right: none;
+ position: relative;
&::after {
.scale-hairline-common(@color, 0, 0, auto, auto);
@@ -63,6 +65,7 @@
html:not([data-scale]) & {
@media (min-resolution: 2dppx) {
border-bottom: none;
+ position: relative;
&::after {
.scale-hairline-common(@color, auto, auto, 0, 0);
@@ -85,6 +88,7 @@
html:not([data-scale]) & {
@media (min-resolution: 2dppx) {
border-left: none;
+ position: relative;
&::before {
.scale-hairline-common(@color, 0, auto, auto, 0);
diff --git a/yarn.lock b/yarn.lock
index 4c05634e..428f0802 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -13752,7 +13752,7 @@ svg-tags@^1.0.0:
resolved "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz#58f71cee3bd519b59d4b2a843b6c7de64ac04764"
integrity sha1-WPcc7jvVGbWdSyqEO2x95krAR2Q=
-svgo@^1.0.0, svgo@^1.2.2:
+svgo@1.3.2, svgo@^1.0.0, svgo@^1.2.2:
version "1.3.2"
resolved "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz#b6dc511c063346c9e415b81e43401145b96d4167"
integrity sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==