|
@@ -4,7 +4,7 @@
|
|
|
import React from 'react';
|
|
|
import { classes } from 'typestyle/lib';
|
|
|
|
|
|
-import { Icon, defaultIcons } from './icon';
|
|
|
+import { Icon } from './icon';
|
|
|
import { camelize } from '../utils';
|
|
|
import { IIconStyle, iconStyle, iconStyleFlat } from '../style/icon';
|
|
|
|
|
@@ -14,12 +14,11 @@ import badSvg from '../../style/icons/bad.svg';
|
|
|
* The icon registry class.
|
|
|
*/
|
|
|
export class IconRegistry {
|
|
|
- constructor(...icons: Icon.IModel[]) {
|
|
|
- if (icons.length) {
|
|
|
- this.addIcon(...icons);
|
|
|
- } else {
|
|
|
- this.addIcon(...defaultIcons);
|
|
|
- }
|
|
|
+ constructor(options?: IconRegistry.IOptions) {
|
|
|
+ this._debug = !!options.debug;
|
|
|
+
|
|
|
+ let icons = options.initialIcons || Icon.defaultIcons;
|
|
|
+ this.addIcon(...icons);
|
|
|
|
|
|
// add the bad state icon
|
|
|
this.addIcon({ name: 'bad', svg: badSvg });
|
|
@@ -36,31 +35,16 @@ export class IconRegistry {
|
|
|
});
|
|
|
}
|
|
|
|
|
|
- resolveName(name: string): string {
|
|
|
- if (!(name in this._svg)) {
|
|
|
- // assume name is really a className, split the className into parts and check each part
|
|
|
- for (let className of name.split(/\s+/)) {
|
|
|
- if (className in this._classNameToName) {
|
|
|
- return this._classNameToName[className];
|
|
|
- }
|
|
|
- }
|
|
|
- // couldn't resolve name, mark as bad
|
|
|
- return 'bad';
|
|
|
- }
|
|
|
-
|
|
|
- return name;
|
|
|
- }
|
|
|
-
|
|
|
/**
|
|
|
* Get the icon as an HTMLElement of tag <svg><svg/>
|
|
|
*/
|
|
|
icon(
|
|
|
props: IconRegistry.IIconOptions & { container: HTMLElement } & IIconStyle
|
|
|
): HTMLElement {
|
|
|
- const { name, className, title, skipbad, container, ...propsStyle } = props;
|
|
|
+ const { name, className, title, container, ...propsStyle } = props;
|
|
|
|
|
|
// if name not in _svg, assume we've been handed a className in place of name
|
|
|
- let svg = this.svg(name, skipbad);
|
|
|
+ let svg = this.resolveSvg(name);
|
|
|
if (!svg) {
|
|
|
// bail
|
|
|
return;
|
|
@@ -78,7 +62,7 @@ export class IconRegistry {
|
|
|
container.appendChild(svgNode);
|
|
|
|
|
|
let styleClass = propsStyle ? iconStyle(propsStyle) : '';
|
|
|
- if (className) {
|
|
|
+ if (className || className === '') {
|
|
|
// override the className, if explicitly passed
|
|
|
container.className = classes(className, styleClass);
|
|
|
} else if (!(styleClass in container.classList)) {
|
|
@@ -103,10 +87,10 @@ export class IconRegistry {
|
|
|
iconReact(
|
|
|
props: IconRegistry.IIconOptions & { tag?: 'div' | 'span' } & IIconStyle
|
|
|
): React.ReactElement {
|
|
|
- const { name, className, title, skipbad, tag, ...propsStyle } = props;
|
|
|
+ const { name, className, title, tag, ...propsStyle } = props;
|
|
|
const Tag = tag || 'div';
|
|
|
|
|
|
- let svg = this.svg(name, skipbad);
|
|
|
+ let svg = this.resolveSvg(name);
|
|
|
if (!svg) {
|
|
|
// bail
|
|
|
return;
|
|
@@ -120,11 +104,26 @@ export class IconRegistry {
|
|
|
);
|
|
|
}
|
|
|
|
|
|
- svg(name: string, skipbad: boolean = false): string {
|
|
|
+ resolveName(name: string): string {
|
|
|
+ if (!(name in this._svg)) {
|
|
|
+ // assume name is really a className, split the className into parts and check each part
|
|
|
+ for (let className of name.split(/\s+/)) {
|
|
|
+ if (className in this._classNameToName) {
|
|
|
+ return this._classNameToName[className];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // couldn't resolve name, mark as bad
|
|
|
+ return 'bad';
|
|
|
+ }
|
|
|
+
|
|
|
+ return name;
|
|
|
+ }
|
|
|
+
|
|
|
+ resolveSvg(name: string): string {
|
|
|
let svgname = this.resolveName(name);
|
|
|
|
|
|
if (svgname === 'bad') {
|
|
|
- if (!skipbad) {
|
|
|
+ if (this._debug) {
|
|
|
// log a warning and mark missing icons with an X
|
|
|
console.error(`Invalid icon name: ${name}`);
|
|
|
} else {
|
|
@@ -136,11 +135,16 @@ export class IconRegistry {
|
|
|
return this._svg[svgname];
|
|
|
}
|
|
|
|
|
|
+ get svg() {
|
|
|
+ return this._svg;
|
|
|
+ }
|
|
|
+
|
|
|
static iconClassName(name: string): string {
|
|
|
return 'jp-' + camelize(name, true) + 'Icon';
|
|
|
}
|
|
|
|
|
|
private _classNameToName: { [key: string]: string } = Object.create(null);
|
|
|
+ private _debug: boolean = false;
|
|
|
private _nameToClassName: { [key: string]: string } = Object.create(null);
|
|
|
private _svg: { [key: string]: string } = Object.create(null);
|
|
|
}
|
|
@@ -160,11 +164,31 @@ export const IconReact = (
|
|
|
};
|
|
|
|
|
|
export namespace IconRegistry {
|
|
|
+ /**
|
|
|
+ * The options used to create an icon registry.
|
|
|
+ */
|
|
|
+ export interface IOptions {
|
|
|
+ /**
|
|
|
+ * The initial icons for the registry.
|
|
|
+ * The [[Icon.defaultIcons]] will be used if not given.
|
|
|
+ */
|
|
|
+ initialIcons?: Icon.IModel[];
|
|
|
+
|
|
|
+ /**
|
|
|
+ * If the debug flag is set, missing icons will raise a warning
|
|
|
+ * and be visually marked with an "X". Otherwise, missing icons
|
|
|
+ * will fail silently.
|
|
|
+ */
|
|
|
+ debug?: boolean;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * The options used to set an icon
|
|
|
+ */
|
|
|
export interface IIconOptions {
|
|
|
name: string;
|
|
|
className?: string;
|
|
|
title?: string;
|
|
|
- skipbad?: boolean;
|
|
|
}
|
|
|
}
|
|
|
|