jupyterlab-contrib / jupyter-ui-toolkit

UI Toolkit for Jupyter
https://jupyterlab-contrib.github.io/jupyter-ui-toolkit/
Other
41 stars 5 forks source link

Create an icon component #119

Closed fcollonval closed 1 month ago

fcollonval commented 1 month ago

Add a new component in https://github.com/jupyterlab-contrib/jupyter-ui-toolkit/tree/main/packages/components/src

You can take inspiration from

The usage should look like that

  1. Create the icon with something like:
const searchIcon =
  '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/></svg>';

registerIcon({ name: "search", svgStr: searchIcon });
  1. Using the component will be:
<jp-icon name="search"></jp-icon>

How to define a new component in the toolkit:

https://www.fast.design/docs/fast-element/declaring-templates#basic-templates

cc @Mehak261124

fcollonval commented 1 month ago

To start the storybook:

  1. Go into the components package packages/components
  2. Execute npm run start

Storybook will open in your browser (first load takes time). And it will auto update each time you change the code.

fcollonval commented 1 month ago

Snippet of the code to create the icon component with the icon registry:

In the index.ts file:

import { FASTElement, customElement, attr, html } from '@microsoft/fast-element';

const template = html<Icon>`
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/></svg>
`;

@customElement({
  name: 'jp-icon',
  template
})
export class Icon extends FASTElement {
  @attr name: string;

  static register(options: {name: string, svgStr: string}): void {}
}

In the story file icon.stories.ts:

// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
import type { StoryFn, Meta, StoryObj } from '@storybook/html';
import { Icon } from './index';

export default {
  title: 'Components/Icon',
  argTypes: {
    name: { control: 'select', options: ['search', ] }
  },
  parameters: {
    actions: {
      disabled: true
    }
  }
} as Meta;

const Template: StoryFn = (args, context): string => {
  return `<jp-icon
    name="${args.name}"
  >
  </jp-icon>`;
};

Icon.register({
  name: 'search',
  svgStr: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/></svg>'
});

export const Default: StoryObj = { render: Template.bind({}) };
Default.args = {
  name: 'search'
};
fcollonval commented 1 month ago

Next step will be to handle the map of icons:

const template = html<Icon>`
 ${x => Icon.get(x.name) ?? 'default icon'}
`;

@customElement({
  name: 'jp-icon',
  template
})
export class Icon extends FASTElement {
  private static iconsMap = new Map<string, string>();

  @attr name: string;

  static register(options: {name: string, svgStr: string}): void {
    if (Icon.iconsMap.has(options.name)) { throw new Error(`Icon with name ${options.name} is already defined`); }

    Icon.iconsMap.set(options.name, options.svgStr);
  }
}