浏览代码

feat(config-provider): 覆盖nut-icon属性 (#2022)

thx125 2 年之前
父节点
当前提交
411548e200

+ 27 - 1
src/packages/__VUE/configprovider/__tests__/configprovider.spec.ts

@@ -1,5 +1,14 @@
-import { mount } from '@vue/test-utils';
+import { config, mount } from '@vue/test-utils';
 import ConfigProvider from '../index.vue';
+import NutIcon from '../../icon/index.vue';
+import { h } from 'vue';
+
+// 所有的测试用例之前执行一次
+beforeAll(() => {
+  config.global.components = {
+    NutIcon
+  };
+});
 
 test('prop theme & tag', async () => {
   const wrapper = mount(ConfigProvider, {
@@ -11,3 +20,20 @@ test('prop theme & tag', async () => {
   const html = expect(wrapper.html());
   expect(wrapper.find<HTMLElement>('.nut-theme-dark'));
 });
+
+test('ConfigProvider prop: fontClassName & classPrefix', async () => {
+  const wrapper = mount(ConfigProvider, {
+    props: {
+      theme: 'dark',
+      tag: 'div',
+      fontClassName: 'iconfont',
+      classPrefix: 'icon'
+    },
+    slots: {
+      default: h(NutIcon, {
+        name: 'close'
+      })
+    }
+  });
+  expect(wrapper.find<HTMLElement>('.iconfont.icon-close'));
+});

+ 3 - 1
src/packages/__VUE/configprovider/doc.en-US.md

@@ -180,4 +180,6 @@ Here are all the base variables:
 |------------|----------------------------------------------------------------------|--------|---------|
 | theme      | Theme style, set to `dark` to enable dark mode, take effect globally | string | -       |
 | theme-vars | Customized theme variable                     | object | -       |
-| tag        | HTML Tag of root element                                             | string | `div`     |
+| tag        | HTML Tag of root element                                             | string | `div`     |
+| font-class-name | Custom icon font base class name                                     | String | `nutui-iconfont` |
+| class-prefix    | Custom icon class name prefix for using custom icons                 | String | `nut-icon`       |

+ 3 - 1
src/packages/__VUE/configprovider/doc.md

@@ -196,4 +196,6 @@ app.use(ConfigProvider);
 |------------|--------------------------------------------------|--------|--------|
 | theme      | 主题风格,设置为 `dark` 来开启深色模式,全局生效 | string | -      |
 | theme-vars | 自定义主题变量                        | object | -      |
-| tag        | 根节点对应的 HTML 标签名                         | string | `div`    |
+| tag        | 根节点对应的 HTML 标签名                         | string | `div`    |
+| font-class-name | 自定义 icon 字体基础类名                           | String | `nutui-iconfont` |
+| class-prefix    | 自定义 icon 类名前缀,用于使用自定义图标              | String | `nut-icon`       |

+ 3 - 1
src/packages/__VUE/configprovider/doc.taro.md

@@ -196,4 +196,6 @@ page, :root {
 |------------|--------------------------------------------------|--------|--------|
 | theme      | 主题风格,设置为 `dark` 来开启深色模式,全局生效 | string | -      |
 | theme-vars | 自定义主题变量                   | object | -      |
-| tag        | 根节点对应的小程序标签名                         | string | `view`    |
+| tag        | 根节点对应的小程序标签名                         | string | `view`    |
+| font-class-name | 自定义 icon 字体基础类名                           | String | `nutui-iconfont` |
+| class-prefix    | 自定义 icon 类名前缀,用于使用自定义图标              | String | `nut-icon`       |

+ 41 - 3
src/packages/__VUE/configprovider/index.taro.vue

@@ -1,12 +1,15 @@
 <script lang="ts">
 import { createComponent } from '@/packages/utils/create';
-import { h, PropType } from 'vue';
+import { h, PropType, VNode } from 'vue';
+import { isObject } from '@/packages/utils/util';
 const { componentName, create } = createComponent('config-provider');
 export default create({
   props: {
     theme: { type: String, default: '' },
     themeVars: { type: Object, default: () => {} },
-    tag: { type: String as PropType<keyof HTMLElementTagNameMap>, default: 'div' }
+    tag: { type: String as PropType<keyof HTMLElementTagNameMap>, default: 'div' },
+    classPrefix: { type: String, default: 'nut-icon' },
+    fontClassName: { type: String, default: 'nutui-iconfont' }
   },
   setup(props: any, { slots }: any) {
     const kebabCase = (str: string): string => {
@@ -57,14 +60,49 @@ export default create({
       });
       return cssVars;
     };
+
+    // 覆盖默认插槽的属性
+    const overDefaultSlotProp = (vnodes: VNode[]) => {
+      if (!vnodes) {
+        return;
+      }
+      vnodes.forEach((vnode: VNode) => {
+        let type = vnode.type;
+        type = (type as any).name || type;
+        if (!vnode.props) {
+          vnode.props = {};
+        }
+        if (type == 'nut-icon') {
+          vnode.props['fontClassName'] = vnode.props['font-class-name'] || props.fontClassName;
+          vnode.props['classPrefix'] = vnode.props['class-prefix'] || props.classPrefix;
+        }
+
+        if (Array.isArray(vnode.children) && vnode.children?.length) {
+          overDefaultSlotProp(vnode.children as VNode[]);
+        } else if (isObject(vnode.children) && Object.keys(vnode.children)) {
+          let children = vnode.children as any;
+          for (const key in children) {
+            if (key === '_') {
+              break;
+            }
+
+            const childrenVNode = children[key]?.();
+            overDefaultSlotProp(childrenVNode);
+            children[key] = () => childrenVNode;
+          }
+        }
+      });
+    };
     return () => {
+      const defaultSlots = slots.default?.();
+      overDefaultSlotProp(defaultSlots);
       return h(
         props.tag,
         {
           class: `nut-theme-${props.theme}`,
           style: mapThemeVarsToCSSVars(props.themeVars)
         },
-        slots.default?.()
+        defaultSlots
       );
     };
   }

+ 38 - 2
src/packages/__VUE/configprovider/index.vue

@@ -1,6 +1,7 @@
 <script lang="ts">
 import { createComponent } from '@/packages/utils/create';
-import { h, nextTick, onMounted, PropType } from 'vue';
+import { h, PropType, VNode } from 'vue';
+import { isObject } from '@/packages/utils/util';
 const { componentName, create } = createComponent('config-provider');
 export default create({
   props: {
@@ -59,14 +60,49 @@ export default create({
 
       return cssVars;
     };
+
+    // 覆盖默认插槽的属性
+    const overDefaultSlotProp = (vnodes: VNode[]) => {
+      if (!vnodes) {
+        return;
+      }
+      vnodes.forEach((vnode: VNode) => {
+        let type = vnode.type;
+        type = (type as any).name || type;
+        if (!vnode.props) {
+          vnode.props = {};
+        }
+        if (type == 'nut-icon') {
+          vnode.props['fontClassName'] = vnode.props['font-class-name'] || props.fontClassName;
+          vnode.props['classPrefix'] = vnode.props['class-prefix'] || props.classPrefix;
+        }
+
+        if (Array.isArray(vnode.children) && vnode.children?.length) {
+          overDefaultSlotProp(vnode.children as VNode[]);
+        } else if (isObject(vnode.children) && Object.keys(vnode.children)) {
+          let children = vnode.children as any;
+          for (const key in children) {
+            if (key === '_') {
+              break;
+            }
+
+            const childrenVNode = children[key]?.();
+            overDefaultSlotProp(childrenVNode);
+            children[key] = () => childrenVNode;
+          }
+        }
+      });
+    };
     return () => {
+      const defaultSlots = slots.default?.();
+      overDefaultSlotProp(defaultSlots);
       return h(
         props.tag,
         {
           class: `nut-theme-${props.theme}`,
           style: mapThemeVarsToCSSVars(props.themeVars)
         },
-        slots.default?.()
+        defaultSlots
       );
     };
   }