telamonian 5 年之前
父节点
当前提交
34ca1ce4c6

+ 53 - 38
packages/ui-components/src/icon/jlicon.tsx

@@ -7,43 +7,28 @@ import { classes } from 'typestyle';
 
 import { iconStyle, IIconStyle } from '../style/icon';
 import { getReactAttrs } from '../utils';
+import { Text } from '@jupyterlab/coreutils';
 
 export class JLIcon {
-  constructor({ name, style = {}, svgstr, _debug = false }: JLIcon.IOptions) {
+  constructor({ name, svgstr, _debug = false }: JLIcon.IOptions) {
     this.name = name;
-    this.style = style;
     this.svgstr = svgstr;
 
+    this._className = 'jp-' + Text.camelCase(name, true) + 'Icon';
     this._debug = _debug;
-  }
-
-  resolveSvg(title?: string): HTMLElement | null {
-    const svgDoc = new DOMParser().parseFromString(
-      this.svgstr,
-      'image/svg+xml'
-    );
-    const svgElement = svgDoc.documentElement;
 
-    if (svgElement.getElementsByTagName('parsererror').length > 0) {
-      const errmsg = `SVG HTML was malformed for icon name: ${name}`;
-      // parse failed, svgElement will be an error box
-      if (this._debug) {
-        // fail noisily, render the error box
-        console.error(errmsg);
-        return svgElement;
-      } else {
-        // bad svg is always a real error, fail silently but warn
-        console.warn(errmsg);
-        return null;
-      }
-    } else {
-      // parse succeeded
-      if (title) {
-        Private.setTitleSvg(svgElement, title);
-      }
+    this.react = this._initReact();
+  }
 
-      return svgElement;
+  className({
+    className,
+    ...propsStyle
+  }: { className?: string } & IIconStyle = {}): string {
+    if (!className) {
+      className = this._className;
     }
+
+    return classes(className, iconStyle(propsStyle));
   }
 
   element({
@@ -53,12 +38,6 @@ export class JLIcon {
     tag = 'div',
     ...propsStyle
   }: JLIcon.IProps = {}): HTMLElement | null {
-    const propsStyleComb = { ...this.style, ...propsStyle };
-    const classNames = classes(
-      className,
-      propsStyleComb ? iconStyle(propsStyleComb) : ''
-    );
-
     // ensure that svg html is valid
     const svgElement = this.resolveSvg(title);
     if (!svgElement) {
@@ -79,18 +58,51 @@ export class JLIcon {
   }
 
   render(host: HTMLElement, props: JLIcon.IProps = {}): void {
-    return ReactDOM.render(<this.react {...props} container={host} />, host);
+    return ReactDOM.render(<this.react {...props} />, host);
+  }
+
+  resolveSvg(title?: string): HTMLElement | null {
+    const svgDoc = new DOMParser().parseFromString(
+      this.svgstr,
+      'image/svg+xml'
+    );
+    const svgElement = svgDoc.documentElement;
+
+    if (svgElement.getElementsByTagName('parsererror').length > 0) {
+      const errmsg = `SVG HTML was malformed for icon name: ${name}`;
+      // parse failed, svgElement will be an error box
+      if (this._debug) {
+        // fail noisily, render the error box
+        console.error(errmsg);
+        return svgElement;
+      } else {
+        // bad svg is always a real error, fail silently but warn
+        console.warn(errmsg);
+        return null;
+      }
+    } else {
+      // parse succeeded
+      if (title) {
+        Private.setTitleSvg(svgElement, title);
+      }
+
+      return svgElement;
+    }
   }
 
   unrender(host: HTMLElement): void {
     ReactDOM.unmountComponentAtNode(host);
   }
 
+  style(props: IIconStyle) {
+    return iconStyle(props);
+  }
+
   protected _initReact() {
     const component = React.forwardRef(
       (
         {
-          className = '',
+          className,
           container,
           title,
           tag = 'div',
@@ -146,11 +158,14 @@ export class JLIcon {
   }
 
   readonly name: string;
-  readonly react = this._initReact();
-  protected style: IIconStyle;
+  readonly react: React.ForwardRefExoticComponent<
+    JLIcon.IProps & React.RefAttributes<SVGElement>
+  >;
   readonly svgstr: string;
 
+  protected _className: string;
   protected _debug: boolean;
+  protected _svgs: { [key: string]: string } = Object.create(null);
 }
 
 /**

+ 7 - 1
packages/ui-components/src/style/icon.ts

@@ -272,6 +272,10 @@ function containerCSS(props: IIconStyle): NestedCSSProperties {
  * for setting the style on the container of an svg node representing an icon
  */
 export const iconStyle = (props: IIconStyle): string => {
+  if (Object.keys(props).length === 0) {
+    return '';
+  }
+
   const conCSS = containerCSS(props);
 
   return style({
@@ -287,5 +291,7 @@ export const iconStyle = (props: IIconStyle): string => {
  * for setting the style directly on the svg node representing an icon
  */
 export const iconStyleFlat = (props: IIconStyle): string => {
-  return style(iconCSS(props));
+  const css = iconCSS(props);
+
+  return css && style(css);
 };

+ 1 - 2
packages/ui-components/src/utils.ts

@@ -12,8 +12,7 @@ export function combineClasses(
 export function getReactAttrs(elem: Element) {
   return elem.getAttributeNames().reduce((d, name) => {
     if (name !== 'style') {
-      const reactName = Text.camelCase(name);
-      d[reactName] = elem.getAttribute(name);
+      d[Text.camelCase(name)] = elem.getAttribute(name);
     }
     return d;
   }, {} as any);