Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions packages/components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
"prepublishOnly": "pnpm build && test -f 'dist/styles/@hashicorp/design-system-components.css' || (echo \"\n\\033[31m⚠️ Error: the pre-compiled CSS file \\`dist/styles/@hashicorp/design-system-components.css\\` was not found\\033[0m\\n\" && exit 1)"
},
"dependencies": {
"@carbon/icon-helpers": "^10.69.0",
"@carbon/icons": "^11.70.0",
"@codemirror/commands": "^6.8.0",
"@codemirror/lang-go": "^6.0.1",
"@codemirror/lang-javascript": "^6.2.2",
Expand Down Expand Up @@ -384,6 +386,7 @@
"./modifiers/hds-code-editor/types.js": "./dist/_app_/modifiers/hds-code-editor/types.js",
"./modifiers/hds-register-event.js": "./dist/_app_/modifiers/hds-register-event.js",
"./modifiers/hds-tooltip.js": "./dist/_app_/modifiers/hds-tooltip.js",
"./services/hds-carbon.js": "./dist/_app_/services/hds-carbon.js",
"./services/hds-intl.js": "./dist/_app_/services/hds-intl.js",
"./services/hds-time.js": "./dist/_app_/services/hds-time.js"
}
Expand Down
8 changes: 8 additions & 0 deletions packages/components/src/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -391,3 +391,11 @@ export { default as HdsInteractive } from './components/hds/interactive/index.ts

// PopoverPrimitive
export { default as HdsPopoverPrimitive } from './components/hds/popover-primitive/index.ts';

// -----------------------------------------------------------
// ### CARBON
// -----------------------------------------------------------

export * from './utils/hds-carbon-icon-map.ts';

export { default as HdsCarbonService } from './services/hds-carbon.ts';
20 changes: 13 additions & 7 deletions packages/components/src/components/hds/icon/index.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,21 @@
role={{this.role}}
width="{{this.svgSize.width}}"
height="{{this.svgSize.height}}"
viewBox="0 0 {{this.size}} {{this.size}}"
viewBox={{this.viewBox}}
xmlns="http://www.w3.org/2000/svg"
{{this.loadCarbonIcon this.hdsCarbon.carbonModeEnabled}}
>
{{#if @title}}
<title id={{this._titleId}}>{{@title}}</title>
<g role="presentation">
<use href="#flight-{{@name}}-{{this.size}}"></use>
</g>
{{#if this.hdsCarbon.carbonModeEnabled}}
{{this.carbonPaths}}
{{else}}
<use href="#flight-{{@name}}-{{this.size}}"></use>
{{#if @title}}
<title id={{this._titleId}}>{{@title}}</title>

<g role="presentation">
<use href="#flight-{{@name}}-{{this.size}}"></use>
</g>
{{else}}
<use href="#flight-{{@name}}-{{this.size}}"></use>
{{/if}}
{{/if}}
</svg>
100 changes: 91 additions & 9 deletions packages/components/src/components/hds/icon/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,22 @@
*/

import Component from '@glimmer/component';
import { service } from '@ember/service';
import { guidFor } from '@ember/object/internals';
import { assert } from '@ember/debug';
import { iconNames } from '@hashicorp/flight-icons/svg';
import { hdsCarbonIcons } from '../../../utils/hds-carbon-icon-map.ts';
import { HdsIconSizeValues, HdsIconColorValues } from './types.ts';
import { task } from 'ember-concurrency';
import { tracked } from '@glimmer/tracking';
import { modifier } from 'ember-modifier';
import { htmlSafe } from '@ember/template';

import type { HdsIconSizes, HdsIconColors } from './types';
import type { IconName } from '@hashicorp/flight-icons/svg';
import type Owner from '@ember/owner';
import type HdsCarbonService from '../../../services/hds-carbon.ts';
import type IconDescriptor from '@carbon/icon-helpers/lib/types';

export const COLORS: HdsIconColors[] = Object.values(HdsIconColorValues);
export const NAMES = iconNames;
Expand All @@ -29,6 +38,10 @@ export interface HdsIconSignature {
}

export default class HdsIcon extends Component<HdsIconSignature> {
@service declare readonly hdsCarbon: HdsCarbonService;

@tracked carbonIcon: IconDescriptor | null = null;

private _iconId = 'icon-' + guidFor(this);
private _titleId = 'title-' + guidFor(this);

Expand Down Expand Up @@ -66,15 +79,32 @@ export default class HdsIcon extends Component<HdsIconSignature> {
}
}

get size(): string {
get size(): HdsIconSizes {
return this.args.size ?? HdsIconSizeValues.Sixteen;
}

get svgSize(): { width: string; height: string } {
return {
width: this.args.stretched ? '100%' : this.size,
height: this.args.stretched ? '100%' : this.size,
};
get svgSize(): {
width: HdsIconSizes | '100%';
height: HdsIconSizes | '100%';
} {
const { stretched } = this.args;

if (stretched) {
return {
width: '100%',
height: '100%',
};
}

let width: HdsIconSizes = this.size;
let height: HdsIconSizes = this.size;

if (this.hdsCarbon.carbonModeEnabled && this.carbonIcon !== null) {
width = this.carbonIcon.attrs!['width']!.toString() as HdsIconSizes;
height = this.carbonIcon.attrs!['height']!.toString() as HdsIconSizes;
}

return { width, height };
}

get title(): string | null {
Expand All @@ -91,10 +121,10 @@ export default class HdsIcon extends Component<HdsIconSignature> {

get classNames() {
const { name } = this.args;

const classes = ['hds-icon'];

// add a class based on the @name argument
classes.push(`hds-icon-${name}`);
classes.push(`hds-icon--${name}`);

if (this.isInline) {
classes.push('hds-icon--is-inline');
Expand All @@ -104,12 +134,64 @@ export default class HdsIcon extends Component<HdsIconSignature> {
if (this.predefinedColor) {
classes.push(`hds-foreground-${this.predefinedColor}`);
}

// add an extra class to control the animation (depends on the icon)
if (['loading', 'running'].includes(name)) {
classes.push(`hds-icon--animation-${name}`);
}

return classes.join(' ');
}

get viewBox(): string | undefined {
if (this.hdsCarbon.carbonModeEnabled && this.carbonIcon !== null) {
return this.carbonIcon.attrs!['viewBox'];
} else {
return `0 0 ${this.size} ${this.size}`;
}
}

get carbonPaths() {
const iconData = this.carbonIcon;

if (!iconData || !iconData.content) {
return null;
}

const svgContent = iconData.content.map((node) => {
const attributes = Object.entries(node.attrs!)
.map(([key, value]) => `${key}="${value}"`)
.join(' ');

return `<${node.elem} ${attributes}></${node.elem}>`;
});

return htmlSafe(svgContent.join(''));
}

loadCarbonIcon = modifier((_element, [carbonModeEnabled]) => {
if (carbonModeEnabled) {
void this.loadCarbonIconTask.perform();
} else {
this.carbonIcon = null;
}
});

loadCarbonIconTask = task(async () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[thought] what if, instead of loading the icons as unstructured JS objects and then build them at runtime as SVG, we leverage the existing icons pipeline to prepare them as Ember component (eg. as .gts template-only SVG files), and then import the component itself? this would also open the doors to do the same for the Flight icons, so you would not need two system of loading/showing icons, but only one.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can directly use the SVG files that are in the package: https://www.npmjs.com/package/@carbon/icons?activeTab=code

And this, I think, would be a valid format for a .gts file:

<template>
  <svg>...</svg>
</template>

(to confirm/validate with the FEF team, I would imagine)

const iconDefinition = (
hdsCarbonIcons as Partial<
Record<
IconName,
(size?: HdsIconSizes) => Promise<{ default: IconDescriptor }>
>
>
)[this.args.name];

if (iconDefinition === undefined) {
return;
}

const iconImport = await iconDefinition(this.size);

this.carbonIcon = iconImport.default;
});
}
15 changes: 15 additions & 0 deletions packages/components/src/services/hds-carbon.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: MPL-2.0
*/

import Service from '@ember/service';
import { tracked } from '@glimmer/tracking';

export default class HdsCarbonService extends Service {
@tracked carbonModeEnabled: boolean = false;

toggleCarbonMode() {
this.carbonModeEnabled = !this.carbonModeEnabled;
}
}
4 changes: 4 additions & 0 deletions packages/components/src/types/carbon-icons.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
declare module '@carbon/icons/es/*' {
const content: unknown;
export default content;
}
Loading
Loading