carbon-components-angular
Version:
Next generation components
425 lines (419 loc) • 16.5 kB
JavaScript
import * as i0 from '@angular/core';
import { Injectable, Directive, Input, Optional, SkipSelf, NgModule } from '@angular/core';
import { toString, getAttributes } from '@carbon/icon-helpers';
import { CommonModule } from '@angular/common';
import Add16 from '@carbon/icons/es/add/16';
import Add20 from '@carbon/icons/es/add/20';
import Bee16 from '@carbon/icons/es/bee/16';
import Bee20 from '@carbon/icons/es/bee/20';
import Calendar16 from '@carbon/icons/es/calendar/16';
import Carbon16 from '@carbon/icons/es/carbon/16';
import Carbon20 from '@carbon/icons/es/carbon/20';
import CaretDown16 from '@carbon/icons/es/caret--down/16';
import CaretLeft16 from '@carbon/icons/es/caret--left/16';
import CaretRight16 from '@carbon/icons/es/caret--right/16';
import CaretUp16 from '@carbon/icons/es/caret--up/16';
import Checkmark16 from '@carbon/icons/es/checkmark/16';
import CheckmarkFilled16 from '@carbon/icons/es/checkmark--filled/16';
import CheckmarkFilled20 from '@carbon/icons/es/checkmark--filled/20';
import CheckmarkOutline16 from '@carbon/icons/es/checkmark--outline/16';
import Checkbox16 from '@carbon/icons/es/checkbox/16';
import CheckboxCheckedFilled16 from '@carbon/icons/es/checkbox--checked--filled/16';
import ChevronDown16 from '@carbon/icons/es/chevron--down/16';
import ChevronRight16 from '@carbon/icons/es/chevron--right/16';
import CircleDash16 from '@carbon/icons/es/circle-dash/16';
import Close16 from '@carbon/icons/es/close/16';
import Close20 from '@carbon/icons/es/close/20';
import Copy16 from '@carbon/icons/es/copy/16';
import Copy20 from '@carbon/icons/es/copy/20';
import Data216 from '@carbon/icons/es/data--2/16';
import Data220 from '@carbon/icons/es/data--2/20';
import Document16 from '@carbon/icons/es/document/16';
import Document20 from '@carbon/icons/es/document/20';
import Download16 from '@carbon/icons/es/download/16';
import ErrorFilled16 from '@carbon/icons/es/error--filled/16';
import ErrorFilled20 from '@carbon/icons/es/error--filled/20';
import Fade16 from '@carbon/icons/es/fade/16';
import Fade20 from '@carbon/icons/es/fade/20';
import Folder16 from '@carbon/icons/es/folder/16';
import Incomplete16 from '@carbon/icons/es/incomplete/16';
import InformationFilled16 from '@carbon/icons/es/information--filled/16';
import InformationFilled20 from '@carbon/icons/es/information--filled/20';
import InformationSquareFilled20 from '@carbon/icons/es/information--square--filled/20';
import Menu16 from '@carbon/icons/es/menu/16';
import Menu20 from '@carbon/icons/es/menu/20';
import OverflowMenuVertical16 from '@carbon/icons/es/overflow-menu--vertical/16';
import OverflowMenuHorizontal16 from '@carbon/icons/es/overflow-menu--horizontal/16';
import Save16 from '@carbon/icons/es/save/16';
import Search16 from '@carbon/icons/es/search/16';
import Settings16 from '@carbon/icons/es/settings/16';
import SettingsAdjust16 from '@carbon/icons/es/settings--adjust/16';
import Subtract16 from '@carbon/icons/es/subtract/16';
import TrashCan16 from '@carbon/icons/es/trash-can/16';
import Warning16 from '@carbon/icons/es/warning/16';
import WarningFilled16 from '@carbon/icons/es/warning--filled/16';
import WarningFilled20 from '@carbon/icons/es/warning--filled/20';
import WarningAltFilled16 from '@carbon/icons/es/warning--alt--filled/16';
import WarningAltFilled20 from '@carbon/icons/es/warning--alt--filled/20';
import View16 from '@carbon/icons/es/view/16';
import ViewOff16 from '@carbon/icons/es/view--off/16';
/**
* Abstract class that represent a cache of icons.
*
* The actual caching mechanism will be implementation specific,
* but it's likely a good idea to key by the icons name and/or size.
* Icon name and size will always be strings, and they will be the two consistent
* identifiers of an icon. For the purposes of storage additonal descriptor properties may
* be used, but the name and size are the only ones guarenteed to be passed for lookup purposes.
*/
class IconCache {
}
/**
* Custom error for when a name can't be found
*/
class IconNameNotFoundError extends Error {
constructor(name) {
super(`Icon ${name} not found`);
}
}
/**
* Custom error for when a specific size can't be found
*/
class IconSizeNotFoundError extends Error {
constructor(size, name) {
super("Size ${size} for ${name} not found");
}
}
/**
* Concrete implementation of `IconCache` as a simple in memory cache
*/
class IconMemoryCache extends IconCache {
constructor() {
super(...arguments);
this.iconMap = new Map();
}
get(name, size) {
if (!this.iconMap.has(name)) {
throw new IconNameNotFoundError(name);
}
const sizeMap = this.iconMap.get(name);
if (!sizeMap.has(size)) {
throw new IconSizeNotFoundError(size, name);
}
return sizeMap.get(size);
}
set(name, size, descriptor) {
if (!this.iconMap.has(name)) {
this.iconMap.set(name, new Map());
}
const sizeMap = this.iconMap.get(name);
sizeMap.set(size, descriptor);
}
}
/**
* The icon service is a singleton service responsible for registering and retriving icons from `@carbon/icons`.
*
* It's important to register icons before use. It's reccommended to register your icons early, likely in your app.component.
*
* To allow for improved tree shaking _do not_ import all the icons from `@carbon/icons` and register them.
* Instead register only the icons in use by your application. If your application makes use of lazy loaded
* modules you may also lazy load the icons used in that module by registering them early on in that module.
*
* `ngOnInit` should be sufficiantly early to register icons.
*
* Example:
* ```
* import { Accessibility16 } from "@carbon/icons";
*
* // ...
*
* class MyComponent implements OnInit {
* constructor(protected iconService: IconService) {}
*
* // ...
*
* ngOnInit() {
* this.iconService.register(Accessibility16);
* }
*
* // ...
* }
* ```
*
* If needed it is possible to register an icon under a different name, via `registerAs`.
*/
class IconService {
constructor() {
this.iconCache = new IconMemoryCache();
}
/**
* Registers an array of icons based on the metadata provided by `@carbon/icons`
*/
registerAll(descriptors) {
descriptors.forEach(icon => this.register(icon));
}
/**
* Registers an icon based on the metadata provided by `@carbon/icons`
*/
register(descriptor) {
const { name } = descriptor;
this.registerAs(name, descriptor);
}
/**
* Registers an icon based on a uniqe name and metadata provided by `@carbon/icons`
*/
registerAs(name, descriptor) {
const { size } = descriptor;
this.iconCache.set(name, size.toString(), descriptor);
}
/**
* Gets an icon, converts it to a string, and caches the result
*/
get(name, size) {
try {
const icon = this.iconCache.get(name, size.toString());
if (!icon.svg) {
icon.svg = toString(icon);
}
return icon;
}
catch (e) {
throw e;
}
}
/**
* Configure various service settings (caching strategy ...)
*/
configure(options) {
this.iconCache = options.cache;
}
}
IconService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: IconService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
IconService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: IconService });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: IconService, decorators: [{
type: Injectable
}] });
/**
* A directive for populating a svg element based on the provided carbon icon name.
*
* Get started with importing the module:
*
* ```typescript
* import { IconModule } from 'carbon-components-angular';
* ```
*
* [See demo](../../?path=/story/components-icon--basic)
*/
class IconDirective {
constructor(elementRef, iconService) {
this.elementRef = elementRef;
this.iconService = iconService;
this.cdsIcon = "";
this.size = "16";
this.title = "";
this.ariaLabel = "";
this.ariaLabelledBy = "";
this.ariaHidden = "";
this.isFocusable = false;
}
/**
* @deprecated since v5 - Use `cdsIcon` input property instead
*/
set ibmIcon(iconName) {
this.cdsIcon = iconName;
}
renderIcon(iconName) {
const root = this.elementRef.nativeElement;
let icon;
try {
icon = this.iconService.get(iconName, this.size.toString());
}
catch (error) {
console.warn(error);
// bail out
return;
}
const domParser = new DOMParser();
const rawSVG = icon.svg;
const svgElement = domParser.parseFromString(rawSVG, "image/svg+xml").documentElement;
let node = root.tagName.toUpperCase() !== "SVG" ? svgElement : svgElement.firstChild;
root.innerHTML = ""; // Clear root element
while (node) {
// importNode makes a clone of the node
// this ensures we keep looping over the nodes in the parsed document
root.appendChild(root.ownerDocument.importNode(node, true));
// type the node because the angular compiler freaks out if it
// ends up thinking it's a `Node` instead of a `ChildNode`
node = node.nextSibling;
}
const svg = root.tagName.toUpperCase() !== "SVG" ? svgElement : root;
const xmlns = "http://www.w3.org/2000/svg";
svg.setAttribute("xmlns", xmlns);
const attributes = getAttributes({
width: icon.attrs.width,
height: icon.attrs.height,
viewBox: icon.attrs.viewBox,
title: this.title,
"aria-label": this.ariaLabel,
"aria-labelledby": this.ariaLabelledBy,
"aria-hidden": this.ariaHidden,
focusable: this.isFocusable.toString(),
fill: icon.attrs.fill
});
const attrKeys = Object.keys(attributes);
for (let i = 0; i < attrKeys.length; i++) {
const key = attrKeys[i];
const value = attributes[key];
if (key === "title") {
continue;
}
if (value) {
svg.setAttribute(key, value);
}
}
if (attributes["title"]) {
const title = document.createElementNS(xmlns, "title");
title.textContent = attributes.title;
IconDirective.titleIdCounter++;
title.setAttribute("id", `${icon.name}-title-${IconDirective.titleIdCounter}`);
// title must be first for screen readers
svg.insertBefore(title, svg.firstElementChild);
svg.setAttribute("aria-labelledby", `${icon.name}-title-${IconDirective.titleIdCounter}`);
}
}
ngAfterViewInit() {
this.renderIcon(this.cdsIcon);
}
ngOnChanges({ cdsIcon }) {
// We want to ignore first change to let the icon register
// and add only after view has been initialized
if (cdsIcon && !cdsIcon.isFirstChange()) {
this.renderIcon(this.cdsIcon);
}
}
}
IconDirective.titleIdCounter = 0;
IconDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: IconDirective, deps: [{ token: i0.ElementRef }, { token: IconService }], target: i0.ɵɵFactoryTarget.Directive });
IconDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.3.0", type: IconDirective, selector: "[cdsIcon], [ibmIcon]", inputs: { ibmIcon: "ibmIcon", cdsIcon: "cdsIcon", size: "size", title: "title", ariaLabel: "ariaLabel", ariaLabelledBy: "ariaLabelledBy", ariaHidden: "ariaHidden", isFocusable: "isFocusable" }, usesOnChanges: true, ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: IconDirective, decorators: [{
type: Directive,
args: [{
selector: "[cdsIcon], [ibmIcon]"
}]
}], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: IconService }]; }, propDecorators: { ibmIcon: [{
type: Input
}], cdsIcon: [{
type: Input
}], size: [{
type: Input
}], title: [{
type: Input
}], ariaLabel: [{
type: Input
}], ariaLabelledBy: [{
type: Input
}], ariaHidden: [{
type: Input
}], isFocusable: [{
type: Input
}] } });
// modules
// either provides a new instance of IconService, or returns the parent
function ICON_SERVICE_PROVIDER_FACTORY(parentService) {
return parentService || new IconService();
}
// icon service *must* be a singleton to ensure that icons are accessible globally and not duplicated
const ICON_SERVICE_PROVIDER = {
provide: IconService,
deps: [[new Optional(), new SkipSelf(), IconService]],
useFactory: ICON_SERVICE_PROVIDER_FACTORY
};
class IconModule {
constructor(iconService) {
this.iconService = iconService;
iconService.registerAll([
Add16,
Add20,
Bee16,
Bee20,
Calendar16,
Carbon16,
Carbon20,
CaretDown16,
CaretLeft16,
CaretRight16,
CaretUp16,
Checkmark16,
CheckmarkFilled16,
CheckmarkFilled20,
CheckmarkOutline16,
Checkbox16,
CheckboxCheckedFilled16,
ChevronDown16,
ChevronRight16,
CircleDash16,
Close16,
Close20,
Copy16,
Copy20,
Data216,
Data220,
Document16,
Document20,
Download16,
ErrorFilled16,
ErrorFilled20,
Fade16,
Fade20,
Folder16,
Incomplete16,
InformationFilled16,
InformationFilled20,
InformationSquareFilled20,
Menu16,
Menu20,
OverflowMenuVertical16,
OverflowMenuHorizontal16,
Save16,
Search16,
Settings16,
SettingsAdjust16,
Subtract16,
TrashCan16,
View16,
ViewOff16,
Warning16,
WarningFilled16,
WarningFilled20,
WarningAltFilled16,
WarningAltFilled20
]);
}
}
IconModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: IconModule, deps: [{ token: IconService }], target: i0.ɵɵFactoryTarget.NgModule });
IconModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "14.3.0", ngImport: i0, type: IconModule, declarations: [IconDirective], imports: [CommonModule], exports: [IconDirective] });
IconModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: IconModule, providers: [
ICON_SERVICE_PROVIDER
], imports: [CommonModule] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: IconModule, decorators: [{
type: NgModule,
args: [{
declarations: [
IconDirective
],
exports: [
IconDirective
],
imports: [
CommonModule
],
providers: [
ICON_SERVICE_PROVIDER
]
}]
}], ctorParameters: function () { return [{ type: IconService }]; } });
/**
* Generated bundle index. Do not edit.
*/
export { ICON_SERVICE_PROVIDER, ICON_SERVICE_PROVIDER_FACTORY, IconCache, IconDirective, IconMemoryCache, IconModule, IconNameNotFoundError, IconService, IconSizeNotFoundError };
//# sourceMappingURL=carbon-components-angular-icon.mjs.map