Browse Source

removed final vestiges of deprecated IconRegistry from codebase

telamonian 5 years ago
parent
commit
07182604be

+ 12 - 22
buildutils/src/ensure-package.ts

@@ -22,20 +22,12 @@ const HEADER_TEMPLATE = `
 
 const ICON_IMPORTS_TEMPLATE = `
 import { JLIcon } from './jlicon';
-import { Icon } from './interfaces';
 
 // icon svg import statements
 {{iconImportStatements}}
 
-// defaultIcons definition
-export namespace IconImports {
-  export const defaultIcons: ReadonlyArray<Icon.IModel> = [
-    {{iconModelDeclarations}}
-  ];
-}
-
-// wrapped icon definitions
-{{wrappedIconDefs}}
+// JLIcon instance construction
+{{jliconConstruction}}
 `;
 
 const ICON_CSS_CLASSES_TEMPLATE = `
@@ -372,8 +364,7 @@ export async function ensureUiComponents(
 
   // build the per-icon import code
   let _iconImportStatements: string[] = [];
-  let _iconModelDeclarations: string[] = [];
-  let _wrappedIconDefs: string[] = [];
+  let _jliconConstruction: string[] = [];
   svgs.forEach(svg => {
     const name = utils.stem(svg);
     const svgpath = path
@@ -381,32 +372,31 @@ export async function ensureUiComponents(
       .split(path.sep)
       .join('/');
 
+    const svgname = utils.camelCase(name) + 'Svg';
+    const iconname = utils.camelCase(name) + 'Icon';
+
     if (dorequire) {
       // load the icon svg using `require`
-      _iconModelDeclarations.push(
-        `{ name: '${name}', svg: require('${svgpath}').default }`
+      _jliconConstruction.push(
+        `export const ${iconname} = new JLIcon({ name: '${name}', svgstr: require('${svgpath}').default });`
       );
     } else {
       // load the icon svg using `import`
-      const svgname = utils.camelCase(name) + 'Svg';
-      const iconname = utils.camelCase(name) + 'Icon';
-
       _iconImportStatements.push(`import ${svgname} from '${svgpath}';`);
-      _iconModelDeclarations.push(`{ name: '${name}', svg: ${svgname} }`);
-      _wrappedIconDefs.push(
+
+      _jliconConstruction.push(
         `export const ${iconname} = new JLIcon({ name: '${name}', svgstr: ${svgname} });`
       );
     }
   });
   const iconImportStatements = _iconImportStatements.join('\n');
-  const iconModelDeclarations = _iconModelDeclarations.join(',\n');
-  const wrappedIconDefs = _wrappedIconDefs.join('\n');
+  const jliconConstruction = _jliconConstruction.join('\n');
 
   // generate the actual contents of the iconImports file
   const iconImportsPath = path.join(iconSrcDir, 'iconimports.ts');
   const iconImportsContents = utils.fromTemplate(
     HEADER_TEMPLATE + ICON_IMPORTS_TEMPLATE,
-    { funcName, iconImportStatements, iconModelDeclarations, wrappedIconDefs }
+    { funcName, iconImportStatements, jliconConstruction }
   );
   messages.push(...ensureFile(iconImportsPath, iconImportsContents, false));
 

+ 4 - 0
packages/theme-light-extension/style/urls.css

@@ -13,6 +13,10 @@
 
   /* blocked by need for stack/carousel */
 
+  /*--jp-icon-check: url('icons/md/checkmark.svg');*/
+  /*--jp-icon-check-disabled: url('icons/md/checkmark-disabled.svg');*/
+  /*--jp-icon-close: url('icons/md/close.svg');*/
+
   /* blocked by lumino interaction*/
 
   /*--jp-icon-check: url('icons/md/checkmark.svg');*/

+ 10 - 6
packages/ui-components-extension/src/index.ts

@@ -6,15 +6,19 @@ import {
   JupyterFrontEndPlugin
 } from '@jupyterlab/application';
 
-import { IIconRegistry, defaultIconRegistry } from '@jupyterlab/ui-components';
+import { IJLIconManager } from '@jupyterlab/ui-components';
 
-const iconRegistry: JupyterFrontEndPlugin<IIconRegistry> = {
-  id: '@jupyterlab/ui-components-extension:default-icon-registry',
-  provides: IIconRegistry,
+/**
+ * Placeholder for future extension that will provide an icon manager class
+ * to assist with overriding/replacing particular sets of icons
+ */
+const jliconManager: JupyterFrontEndPlugin<IJLIconManager> = {
+  id: '@jupyterlab/ui-components-extension:jlicon-manager',
+  provides: IJLIconManager,
   autoStart: true,
   activate: (app: JupyterFrontEnd) => {
-    return defaultIconRegistry;
+    return Object.create(null);
   }
 };
 
-export default iconRegistry;
+export default jliconManager;

+ 1 - 75
packages/ui-components/src/icon/iconimports.ts

@@ -6,7 +6,6 @@
 /* This file was auto-generated by ensureUiComponents() in @jupyterlab/buildutils */
 
 import { JLIcon } from './jlicon';
-import { Icon } from './interfaces';
 
 // icon svg import statements
 import caretDownEmptyThinSvg from '../../style/icons/arrow/caret-down-empty-thin.svg';
@@ -56,7 +55,6 @@ import bugSvg from '../../style/icons/toolbar/bug.svg';
 import checkSvg from '../../style/icons/toolbar/check.svg';
 import circleEmptySvg from '../../style/icons/toolbar/circle-empty.svg';
 import circleSvg from '../../style/icons/toolbar/circle.svg';
-import closeCircleSvg from '../../style/icons/toolbar/close-circle.svg';
 import closeSvg from '../../style/icons/toolbar/close.svg';
 import copySvg from '../../style/icons/toolbar/copy.svg';
 import cutSvg from '../../style/icons/toolbar/cut.svg';
@@ -75,78 +73,7 @@ import searchSvg from '../../style/icons/toolbar/search.svg';
 import stopSvg from '../../style/icons/toolbar/stop.svg';
 import undoSvg from '../../style/icons/toolbar/undo.svg';
 
-// defaultIcons definition
-export namespace IconImports {
-  export const defaultIcons: ReadonlyArray<Icon.IModel> = [
-    { name: 'caret-down-empty-thin', svg: caretDownEmptyThinSvg },
-    { name: 'caret-down-empty', svg: caretDownEmptySvg },
-    { name: 'caret-down', svg: caretDownSvg },
-    { name: 'caret-left', svg: caretLeftSvg },
-    { name: 'caret-right', svg: caretRightSvg },
-    { name: 'caret-up-empty-thin', svg: caretUpEmptyThinSvg },
-    { name: 'caret-up', svg: caretUpSvg },
-    { name: 'console', svg: consoleSvg },
-    { name: 'file', svg: fileSvg },
-    { name: 'folder', svg: folderSvg },
-    { name: 'html5', svg: html5Svg },
-    { name: 'image', svg: imageSvg },
-    { name: 'inspector', svg: inspectorSvg },
-    { name: 'json', svg: jsonSvg },
-    { name: 'keyboard', svg: keyboardSvg },
-    { name: 'launcher', svg: launcherSvg },
-    { name: 'markdown', svg: markdownSvg },
-    { name: 'notebook', svg: notebookSvg },
-    { name: 'python', svg: pythonSvg },
-    { name: 'r-kernel', svg: rKernelSvg },
-    { name: 'react', svg: reactSvg },
-    { name: 'settings', svg: settingsSvg },
-    { name: 'spreadsheet', svg: spreadsheetSvg },
-    { name: 'text-editor', svg: textEditorSvg },
-    { name: 'vega', svg: vegaSvg },
-    { name: 'yaml', svg: yamlSvg },
-    { name: 'jupyter-favicon', svg: jupyterFaviconSvg },
-    { name: 'jupyter', svg: jupyterSvg },
-    { name: 'jupyterlab-wordmark', svg: jupyterlabWordmarkSvg },
-    { name: 'case-sensitive', svg: caseSensitiveSvg },
-    { name: 'regex', svg: regexSvg },
-    { name: 'build', svg: buildSvg },
-    { name: 'extension', svg: extensionSvg },
-    { name: 'palette', svg: paletteSvg },
-    { name: 'running', svg: runningSvg },
-    { name: 'tab', svg: tabSvg },
-    { name: 'kernel', svg: kernelSvg },
-    { name: 'line-form', svg: lineFormSvg },
-    { name: 'list', svg: listSvg },
-    { name: 'not-trusted', svg: notTrustedSvg },
-    { name: 'terminal', svg: terminalSvg },
-    { name: 'trusted', svg: trustedSvg },
-    { name: 'add', svg: addSvg },
-    { name: 'bug', svg: bugSvg },
-    { name: 'check', svg: checkSvg },
-    { name: 'circle-empty', svg: circleEmptySvg },
-    { name: 'circle', svg: circleSvg },
-    { name: 'close-circle', svg: closeCircleSvg },
-    { name: 'close', svg: closeSvg },
-    { name: 'copy', svg: copySvg },
-    { name: 'cut', svg: cutSvg },
-    { name: 'download', svg: downloadSvg },
-    { name: 'edit', svg: editSvg },
-    { name: 'ellipses', svg: ellipsesSvg },
-    { name: 'file-upload', svg: fileUploadSvg },
-    { name: 'filter-list', svg: filterListSvg },
-    { name: 'link', svg: linkSvg },
-    { name: 'new-folder', svg: newFolderSvg },
-    { name: 'paste', svg: pasteSvg },
-    { name: 'refresh', svg: refreshSvg },
-    { name: 'run', svg: runSvg },
-    { name: 'save', svg: saveSvg },
-    { name: 'search', svg: searchSvg },
-    { name: 'stop', svg: stopSvg },
-    { name: 'undo', svg: undoSvg }
-  ];
-}
-
-// wrapped icon definitions
+// JLIcon instance construction
 export const caretDownEmptyThinIcon = new JLIcon({ name: 'caret-down-empty-thin', svgstr: caretDownEmptyThinSvg });
 export const caretDownEmptyIcon = new JLIcon({ name: 'caret-down-empty', svgstr: caretDownEmptySvg });
 export const caretDownIcon = new JLIcon({ name: 'caret-down', svgstr: caretDownSvg });
@@ -194,7 +121,6 @@ export const bugIcon = new JLIcon({ name: 'bug', svgstr: bugSvg });
 export const checkIcon = new JLIcon({ name: 'check', svgstr: checkSvg });
 export const circleEmptyIcon = new JLIcon({ name: 'circle-empty', svgstr: circleEmptySvg });
 export const circleIcon = new JLIcon({ name: 'circle', svgstr: circleSvg });
-export const closeCircleIcon = new JLIcon({ name: 'close-circle', svgstr: closeCircleSvg });
 export const closeIcon = new JLIcon({ name: 'close', svgstr: closeSvg });
 export const copyIcon = new JLIcon({ name: 'copy', svgstr: copySvg });
 export const cutIcon = new JLIcon({ name: 'cut', svgstr: cutSvg });

+ 0 - 302
packages/ui-components/src/icon/iconregistry.tsx

@@ -1,302 +0,0 @@
-// Copyright (c) Jupyter Development Team.
-// Distributed under the terms of the Modified BSD License.
-
-import React from 'react';
-
-import { Text } from '@jupyterlab/coreutils';
-
-import { IIconRegistry, Icon } from './interfaces';
-import { IconImports } from './iconimports';
-import { iconStyle, iconStyleFlat } from '../style/icon';
-import { classes } from '../utils';
-
-import badSvg from '../../style/debug/bad.svg';
-import blankSvg from '../../style/debug/blank.svg';
-
-/**
- * The icon registry class.
- */
-export class IconRegistry implements IIconRegistry {
-  constructor(options: IconRegistry.IOptions = {}) {
-    this._debug = !!options.debug;
-
-    let icons = options.initialIcons || IconImports.defaultIcons;
-    this.addIcon(...icons);
-
-    // add the bad state and blank icons
-    this.addIcon(
-      { name: 'bad', svg: badSvg },
-      { name: 'blank', svg: blankSvg }
-    );
-  }
-
-  addIcon(...icons: Icon.IModel[]): void {
-    icons.forEach((icon: Icon.IModel) => {
-      let className = icon.className
-        ? icon.className
-        : IconRegistry.iconClassName(icon.name);
-      this._classNameToName[className] = icon.name;
-      this._nameToClassName[icon.name] = className;
-      this._svg[icon.name] = icon.svg;
-    });
-  }
-
-  contains(name: string): boolean {
-    return !!this._resolveName(name);
-  }
-
-  /**
-   * Get the icon as an HTMLElement of tag <svg><svg/>
-   */
-  icon(
-    props: Icon.INodeOptions & { container?: HTMLElement }
-  ): HTMLElement | null {
-    const {
-      name,
-      className,
-      title,
-      fallback,
-      container,
-      ...propsStyle
-    } = props;
-
-    // we may have been handed a className in place of name
-    let resolvedName = this.resolveName(name);
-    if (!resolvedName) {
-      // TODO: remove fallback in jlab 2.0
-      if (fallback) {
-        if (container) {
-          container.textContent = title || '';
-          container.className = classes(name, className);
-          return container;
-        } else {
-          // the non-container fallback isn't implemented
-          console.error('unimplemented');
-          return null;
-        }
-      }
-
-      // bail if failing silently
-      return null;
-    }
-
-    // check if icon element is already set
-    if (
-      container &&
-      container.dataset.icon &&
-      container.dataset.icon === resolvedName &&
-      container.children[0]
-    ) {
-      // return the existing icon element
-      return container.children[0] as HTMLElement;
-    }
-
-    // ensure that svg html is valid
-    const svgElement = this.resolveSvg(resolvedName);
-    if (!svgElement) {
-      // bail if failing silently
-      return null;
-    }
-
-    if (title) {
-      Private.setTitleSvg(svgElement, title);
-    }
-
-    if (container) {
-      // clear any existing icon in container (and all other child elements)
-      container.textContent = '';
-      container.dataset.icon = resolvedName;
-      container.appendChild(svgElement);
-
-      let styleClass = propsStyle ? iconStyle(propsStyle) : '';
-      if (className || className === '') {
-        // override the className, if explicitly passed
-        container.className = classes(className, styleClass);
-      } else if (!container.classList.contains(styleClass)) {
-        // add icon styling class to the container's class, if not already present
-        container.className = classes(container.className, styleClass);
-      }
-    } else {
-      // add icon styling class directly to the svg node
-      svgElement.setAttribute(
-        'class',
-        classes(className, propsStyle ? iconStyleFlat(propsStyle) : '')
-      );
-    }
-
-    return svgElement;
-  }
-
-  /**
-   * Get the icon as a ReactElement of tag <tag><svg><svg/><tag/>
-   * TODO: figure out how to remove the unnecessary outer <tag>
-   */
-  iconReact(
-    props: Icon.INodeOptions & { tag?: 'div' | 'span' }
-  ): React.ReactElement {
-    const { name, className, title, fallback, tag, ...propsStyle } = props;
-    const Tag = tag || 'div';
-
-    // we may have been handed a className in place of name
-    const resolvedName = this.resolveName(name);
-    if (!resolvedName) {
-      // TODO: remove fallback in jlab 2.0
-      if (fallback) {
-        return <Tag className={classes(name, className)}>{title || ''}</Tag>;
-      }
-
-      // bail if failing silently
-      return <></>;
-    }
-
-    // ensure that svg html is valid
-    const svgElement = this.resolveSvg(resolvedName);
-    if (!svgElement) {
-      // bail if failing silently
-      return <></>;
-    }
-
-    if (title) {
-      Private.setTitleSvg(svgElement, title);
-    }
-
-    return (
-      <Tag
-        className={classes(className, propsStyle ? iconStyle(propsStyle) : '')}
-        data-icon={resolvedName}
-        dangerouslySetInnerHTML={{
-          __html: svgElement.outerHTML
-        }}
-      />
-    );
-  }
-
-  private _resolveName(name: string): string {
-    if (!(name in this._svg)) {
-      // skip resolution if name is not defined
-      if (name) {
-        // 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, fail silently
-      return '';
-    }
-
-    return name;
-  }
-
-  resolveName(name: string): string {
-    const resolvedName = this._resolveName(name);
-    if (!resolvedName) {
-      if (this._debug) {
-        // couldn't resolve name, mark as bad and warn
-        console.error(`Invalid icon name: ${name}`);
-        return 'bad';
-      } else {
-        // couldn't resolve name, fail silently
-        return '';
-      }
-    }
-
-    return resolvedName;
-  }
-
-  resolveSvg(name: string): HTMLElement | null {
-    let svgHtml = this.svg(name);
-
-    // workaround for 1.0.x versions of Jlab pulling in 1.1.x versions of ui-components
-    // TODO: delete workaround in v2.0.0
-    const bprefix = 'data:image/svg+xml;base64,';
-    if (svgHtml.startsWith(bprefix)) {
-      // slice off the prefix and covert base64 to string
-      svgHtml = atob(svgHtml.slice(bprefix.length));
-    }
-
-    const parser = new DOMParser();
-    const svgElement = parser.parseFromString(svgHtml, 'image/svg+xml')
-      .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
-      return svgElement;
-    }
-  }
-
-  svg(name: string): string {
-    return this._svg[name];
-  }
-
-  static iconClassName(name: string): string {
-    return 'jp-' + Text.camelCase(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);
-}
-
-/**
- * The defaultIconRegistry instance.
- */
-export const defaultIconRegistry: IconRegistry = new IconRegistry();
-
-/**
- * Alias for defaultIconRegistry.iconReact that can be used as a React component
- */
-export const DefaultIconReact = (
-  props: Icon.INodeOptions & { tag?: 'div' | 'span' }
-): React.ReactElement => {
-  return defaultIconRegistry.iconReact(props);
-};
-
-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;
-  }
-}
-
-namespace Private {
-  export function setTitleSvg(svgNode: HTMLElement, title: string): void {
-    // add a title node to the top level svg node
-    let titleNodes = svgNode.getElementsByTagName('title');
-    if (titleNodes.length) {
-      titleNodes[0].textContent = title;
-    } else {
-      let titleNode = document.createElement('title');
-      titleNode.textContent = title;
-      svgNode.appendChild(titleNode);
-    }
-  }
-}

+ 0 - 2
packages/ui-components/src/icon/index.ts

@@ -3,6 +3,4 @@
 
 export * from './iconimports';
 export * from './jlicon';
-export * from './iconregistry';
-export * from './interfaces';
 export * from './tabbarsvg';

+ 0 - 102
packages/ui-components/src/icon/interfaces.ts

@@ -1,102 +0,0 @@
-// Copyright (c) Jupyter Development Team.
-// Distributed under the terms of the Modified BSD License.
-
-import { Token } from '@lumino/coreutils';
-
-import { IIconStyle } from '../style/icon';
-import React from 'react';
-
-/**
- * The interface for an object that keeps a registry of inline
- * svg icons. Has methods for setting up inline svg icons as
- * either `HTMLElement` or `ReactElement`
- */
-export interface IIconRegistry {
-  /**
-   * Add the raw text representation of an svg icon to this registry
-   */
-  addIcon(...icons: Icon.IModel[]): void;
-
-  /**
-   * Check if any icon of name `name` has been registered.
-   * Exact matches only
-   */
-  contains(name: string): boolean;
-
-  /**
-   * Get the icon as an HTMLElement of tag <svg><svg/>
-   */
-  icon(
-    props: Icon.INodeOptions & { container: HTMLElement }
-  ): HTMLElement | null;
-
-  /**
-   * Get the icon as a ReactElement of tag <tag><svg><svg/><tag/>
-   */
-  iconReact(
-    props: Icon.INodeOptions & { tag?: 'div' | 'span' }
-  ): React.ReactElement;
-}
-
-/**
- * The IIconRegistry token.
- */
-export const IIconRegistry = new Token<IIconRegistry>(
-  '@jupyterlab/ui-components:IIconRegistry'
-);
-
-export namespace Icon {
-  /**
-   * A representation of the resources underlying an inline svg icon
-   */
-  export interface IModel {
-    /**
-     * The icon name. For a 'foo-bar.svg' file, the icon name is 'foo-bar'.
-     */
-    name: string;
-
-    /**
-     * Manually set the className corresponding to the icon name. By default,
-     * the className is generated from the name: 'foo-bar' -> 'jp-FooBarIcon'
-     */
-    className?: string;
-
-    /**
-     * A string containing the html corresponding to an SVG element
-     */
-    svg: string;
-  }
-
-  /**
-   * The options used when creating an icon node
-   */
-  export interface INodeOptions extends IIconStyle {
-    /**
-     * The icon name. For a 'foo-bar.svg' file, the icon name is 'foo-bar'.
-     * For backwards compatibility, 'jp-FooBarIcon' is also a valid icon name.
-     *
-     * TODO: until Jlab 2.0
-     * If fallback is set, the name is added to the className
-     * of the resulting icon node
-     */
-    name: string;
-
-    /**
-     * Extra classNames, used in addition to the typestyle className
-     */
-    className?: string;
-
-    /**
-     * Icon title
-     */
-    title?: string;
-
-    /**
-     * If true, if icon name resolution fails, fallback to old
-     * icon handling behavior.
-     *
-     * TODO: remove in Jlab 2.0
-     */
-    fallback?: boolean;
-  }
-}

+ 1 - 0
packages/ui-components/src/index.ts

@@ -4,4 +4,5 @@
 export * from './blueprint';
 export * from './components';
 export * from './icon';
+export * from './tokens';
 export * from './utils';

+ 17 - 0
packages/ui-components/src/tokens.ts

@@ -0,0 +1,17 @@
+// Copyright (c) Jupyter Development Team.
+// Distributed under the terms of the Modified BSD License.
+
+import { Token } from '@lumino/coreutils';
+
+/**
+ * Placeholder for future icon manager class to assist with
+ * overriding/replacing particular sets of icons
+ */
+export interface IJLIconManager {}
+
+/**
+ * The IJLIconManager token.
+ */
+export const IJLIconManager = new Token<IJLIconManager>(
+  '@jupyterlab/ui-components:IJLIconManager'
+);

+ 0 - 4
packages/ui-components/style/deprecated.css

@@ -59,7 +59,6 @@
   --jp-icon-check: url('icons/toolbar/check.svg');
   --jp-icon-circle-empty: url('icons/toolbar/circle-empty.svg');
   --jp-icon-circle: url('icons/toolbar/circle.svg');
-  --jp-icon-close-circle: url('icons/toolbar/close-circle.svg');
   --jp-icon-close: url('icons/toolbar/close.svg');
   --jp-icon-copy: url('icons/toolbar/copy.svg');
   --jp-icon-cut: url('icons/toolbar/cut.svg');
@@ -222,9 +221,6 @@
 .jp-CircleIcon {
   background-image: var(--jp-icon-circle);
 }
-.jp-CloseCircleIcon {
-  background-image: var(--jp-icon-close-circle);
-}
 .jp-CloseIcon {
   background-image: var(--jp-icon-close);
 }

+ 0 - 8
packages/ui-components/style/icons/toolbar/close-circle.svg

@@ -1,8 +0,0 @@
-<svg viewBox="0 0 24 24" height="16" width="16" xmlns="http://www.w3.org/2000/svg">
-  <g class="jp-icon3 jp-icon-selectable jp-icon-accent2-hover" fill="#616161">
-    <circle cx="12" cy="12" r="11"/>
-  </g>
-  <g class="jp-icon-accent2 jp-icon-selectable-inverse jp-icon3-hover" fill="#fff">
-    <path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/>
-  </g>
-</svg>