diff --git a/projects/igniteui-angular-elements/src/app/app.module.ts b/projects/igniteui-angular-elements/src/app/app.module.ts index 56fa430a99a..1e4c09cc437 100644 --- a/projects/igniteui-angular-elements/src/app/app.module.ts +++ b/projects/igniteui-angular-elements/src/app/app.module.ts @@ -31,6 +31,7 @@ import { registerConfig } from "../analyzer/elements.config"; import { createIgxCustomElement } from './create-custom-element'; import { IgxGridStateComponent } from '../lib/state.component'; import { ELEMENTS_TOKEN } from 'igniteui-angular/src/lib/core/utils'; +import { IgxIconBroadcastService } from '../lib/icon.broadcast.service'; @NgModule({ imports: [ @@ -38,13 +39,14 @@ import { ELEMENTS_TOKEN } from 'igniteui-angular/src/lib/core/utils'; BrowserAnimationsModule ], providers: [ - { provide: ELEMENTS_TOKEN, useValue: true } + { provide: ELEMENTS_TOKEN, useValue: true }, + IgxIconBroadcastService ], // bootstrap: [] }) export class AppModule { - constructor(private injector: Injector) {} + constructor(private injector: Injector, private _iconBroadcast: IgxIconBroadcastService) {} ngDoBootstrap() { diff --git a/projects/igniteui-angular-elements/src/index.html b/projects/igniteui-angular-elements/src/index.html index 4bd12923148..e1585a6c320 100644 --- a/projects/igniteui-angular-elements/src/index.html +++ b/projects/igniteui-angular-elements/src/index.html @@ -73,6 +73,7 @@

Flat Grid (column groups, toolbar, paginator, row Medium Large +

Standalone paginator

@@ -112,10 +113,18 @@

Flat Grid (MRL column layout)

import { html, nothing } from "/lit-html.js"; import { Directive, directive } from "/directive.js"; - import { defineComponents, IgcSelectComponent, IgcComboComponent, IgcButtonComponent, IgcButtonGroupComponent } from "igniteui-webcomponents"; + import { defineComponents, IgcSelectComponent, IgcComboComponent, IgcButtonComponent, IgcButtonGroupComponent, registerIconFromText, setIconRef } from "igniteui-webcomponents"; // import "igniteui-webcomponents/themes/light/bootstrap.css"; defineComponents(IgcSelectComponent, IgcComboComponent, IgcButtonComponent, IgcButtonGroupComponent); + + + const buildIcon = + ''; + const thumbUpIcon = + ''; + let icon = thumbUpIcon; + const grid1 = document.getElementById('grid1'); const grid2 = document.getElementById('grid2'); const buttonGroup = document.querySelector('igc-button-group'); @@ -196,6 +205,7 @@

Flat Grid (MRL column layout)

document.getElementById("saveState").addEventListener("click", saveState); document.getElementById("restoreState").addEventListener("click", restoreState); + document.getElementById("toggleIcon").addEventListener("click", toggleIcon); const stateComponent = document.getElementById('state'); stateComponent.options = { paging: false @@ -220,6 +230,25 @@

Flat Grid (MRL column layout)

stateComponent.applyState(obj); } } + + function toggleIcon() { + if (icon !== thumbUpIcon) { + icon = thumbUpIcon; + registerIconFromText("filter_list", thumbUpIcon, "customSet"); + setIconRef('filter_list', 'default', { + name: 'filter_list', + collection: 'customSet', + }); + + } else { + icon = buildIcon; + registerIconFromText("filter_list", buildIcon, "customSet2"); + setIconRef('filter_list', 'default', { + name: 'filter_list', + collection: 'customSet2', + }); + } + } diff --git a/projects/igniteui-angular-elements/src/lib/icon.broadcast.service.spec.ts b/projects/igniteui-angular-elements/src/lib/icon.broadcast.service.spec.ts new file mode 100644 index 00000000000..c58d9970e03 --- /dev/null +++ b/projects/igniteui-angular-elements/src/lib/icon.broadcast.service.spec.ts @@ -0,0 +1,115 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { ActionType, BroadcastIconsChangeMessage, IgxIconBroadcastService, SvgIcon, } from './icon.broadcast.service'; +import { Component, SecurityContext } from '@angular/core'; +import { IconMeta, IgxIconService } from 'igniteui-angular'; +import { wait } from 'igniteui-angular/src/lib/test-utils/ui-interactions.spec'; + +describe('Icon broadcast service', () => { + let fixture: ComponentFixture; + let broadcastChannel: BroadcastChannel; + let events: BroadcastIconsChangeMessage[] = []; + const buildIcon = + ''; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [], + providers: [IgxIconBroadcastService] + }) + .compileComponents(); + }); + + beforeEach(() => { + broadcastChannel = new BroadcastChannel("ignite-ui-icon-channel"); + broadcastChannel.onmessage = (e: MessageEvent) => { + events.push(e.data); + } + fixture = TestBed.createComponent(BroadcastServiceComponent); + }); + + afterEach(() => { + events = []; + broadcastChannel.close(); + }); + + describe('Broadcast Events', () => { + it('should correctly process event of icons registering on channel.', async() => { + // simulate a new icon being registered on channel + const icons: Map> = new Map(); + const icon: Map = new Map() + icon.set("customIcon", { svg: buildIcon }); + icons.set("customCollection", icon); + const message: BroadcastIconsChangeMessage = { + actionType: ActionType.RegisterIcon, + collections: icons + }; + broadcastChannel.postMessage(message); + fixture.detectChanges(); + await wait(50); + fixture.detectChanges(); + const iconService = fixture.componentInstance.iconService; + const svg = iconService.getSvgIcon("customIcon", "customCollection"); + expect(svg).not.toBeUndefined(); + }); + + it('should correctly process event of setting an icon reference on channel.', async() => { + const refs: Map> = new Map(); + const ref: Map = new Map() + ref.set("customIcon", {name: "customIcon", family: "customCollection" }); + refs.set("customCollection", ref); + const message: BroadcastIconsChangeMessage = { + actionType: ActionType.RegisterIcon, + references: refs + }; + broadcastChannel.postMessage(message); + fixture.detectChanges(); + await wait(50); + fixture.detectChanges(); + + const iconService = fixture.componentInstance.iconService; + const serviceRef = iconService.getIconRef("customIcon", "customCollection"); + expect(serviceRef.family).toBe("customCollection"); + expect(serviceRef.name).toBe("customIcon"); + }); + + it('should send a request to sync state from any peer already on the channel on init.', async() => { + await wait(50); + expect(events.length).toBe(1); + expect(events[0].actionType).toBe(ActionType.SyncState); + }); + + it('should correctly process event of synching full state of icons on channel.', async() => { + const icons: Map> = new Map(); + const icon: Map = new Map() + icon.set("customIcon", { svg: buildIcon }); + icons.set("customCollection", icon); + const refs: Map> = new Map(); + const ref: Map = new Map() + ref.set("customIcon", {name: "customIcon", family: "customCollection" }); + refs.set("customCollection", ref); + const message: BroadcastIconsChangeMessage = { + actionType: ActionType.SyncState, + collections: icons, + references: refs + }; + broadcastChannel.postMessage(message); + await wait(50); + const iconService = fixture.componentInstance.iconService; + const svg = iconService.getSvgIcon("customIcon", "customCollection"); + expect(svg).not.toBeUndefined(); + const serviceRef = iconService.getIconRef("customIcon", "customCollection"); + expect(serviceRef.family).toBe("customCollection"); + expect(serviceRef.name).toBe("customIcon"); + }); + }) +}); + +@Component({ + template: ` + `, + standalone: true, + providers: [IgxIconBroadcastService, IgxIconService] +}) +export class BroadcastServiceComponent { + constructor(public iconBroadcast: IgxIconBroadcastService, public iconService: IgxIconService) {} +} diff --git a/projects/igniteui-angular-elements/src/lib/icon.broadcast.service.ts b/projects/igniteui-angular-elements/src/lib/icon.broadcast.service.ts new file mode 100644 index 00000000000..34a32a8c4d6 --- /dev/null +++ b/projects/igniteui-angular-elements/src/lib/icon.broadcast.service.ts @@ -0,0 +1,82 @@ +import { Injectable, Optional } from '@angular/core'; +import { PlatformUtil } from '../../../igniteui-angular/src/lib/core/utils'; +import { IconMeta, IgxIconService } from '../../../igniteui-angular/src/lib/icon/icon.service'; + + +export interface SvgIcon { + svg: string; + title?: string; + } + + export type Collection = Map; + + export enum ActionType { + SyncState = 0, + RegisterIcon = 1, + UpdateIconReference = 2, + } + + export interface BroadcastIconsChangeMessage { + actionType: ActionType; + collections?: Collection>; + references?: Collection>; + } + +/** @hidden @internal **/ +@Injectable() +export class IgxIconBroadcastService { + private iconBroadcastChannel: BroadcastChannel; + constructor( + protected _iconService: IgxIconService, + @Optional() private _platformUtil: PlatformUtil + ) { + if (this._platformUtil?.isBrowser) { + // open broadcast channel for sync with wc icon service. + this.iconBroadcastChannel = new BroadcastChannel("ignite-ui-icon-channel"); + this.iconBroadcastChannel.onmessage = (event) => { + const message = event.data as BroadcastIconsChangeMessage; + if (message.actionType === ActionType.SyncState || + message.actionType === ActionType.RegisterIcon) { + this.updateIconsFromCollection(message.collections); + } + + if (message.actionType === ActionType.SyncState || + message.actionType === ActionType.UpdateIconReference) { + this.updateRefsFromCollection(message.references); + } + }; + // send message to sync state + this.iconBroadcastChannel.postMessage({ + actionType: ActionType.SyncState + }); + } + } + + private updateIconsFromCollection(collections: Collection>) { + if (!collections) return; + const collectionKeys = collections.keys(); + for (const collectionKey of collectionKeys) { + const collection = collections.get(collectionKey); + for (const iconKey of collection.keys()) { + const value = collection.get(iconKey).svg; + this._iconService.addSvgIconFromText(iconKey, value, collectionKey); + } + } + } + + private updateRefsFromCollection(collections: Collection>) { + if (!collections) return; + const collectionKeys = collections.keys(); + for (const collectionKey of collectionKeys) { + const collection = collections.get(collectionKey); + for (const iconKey of collection.keys()) { + const collectionName = collection.get(iconKey).collection; + this._iconService.setIconRef(iconKey, 'default', { + family: collectionName, + name: iconKey + }); + } + } + } + +}