Browse Source

feat: add a lazy style handler and provider factory

pull/4133/head
Arman Ozak 6 years ago
parent
commit
03c077ee2f
  1. 1
      npm/ng-packs/packages/theme-shared/src/lib/handlers/index.ts
  2. 96
      npm/ng-packs/packages/theme-shared/src/lib/handlers/lazy-style.handler.ts
  3. 66
      npm/ng-packs/packages/theme-shared/src/lib/tests/lazy-style.handler.spec.ts

1
npm/ng-packs/packages/theme-shared/src/lib/handlers/index.ts

@ -1 +1,2 @@
export * from './error.handler';
export * from './lazy-style.handler';

96
npm/ng-packs/packages/theme-shared/src/lib/handlers/lazy-style.handler.ts

@ -0,0 +1,96 @@
import {
getLocaleDirection,
LazyLoadService,
LOADING_STRATEGY,
LocalizationService,
} from '@abp/ng.core';
import { Injectable, Injector } from '@angular/core';
import { map, startWith } from 'rxjs/operators';
import { BOOTSTRAP } from '../constants/styles';
import { LocaleDirection } from '../models/common';
import { LAZY_STYLES } from '../tokens/lazy-styles.token';
@Injectable({
providedIn: 'root',
})
export class LazyStyleHandler {
private lazyLoad: LazyLoadService;
private styles: string[];
private _dir: LocaleDirection = 'ltr';
set dir(dir: LocaleDirection) {
if (dir === this._dir) return;
this.switchCSS(dir);
this.setHtmlDir(dir);
this._dir = dir;
}
get dir(): LocaleDirection {
return this._dir;
}
constructor(injector: Injector) {
this.setStyles(injector);
this.setLazyLoad(injector);
this.listenToLanguageChanges(injector);
}
private getLoadedBootstrap(): LoadedStyle {
const href = createLazyStyleHref(BOOTSTRAP, this.dir);
const selector = `[href$="${href}"]`;
const link = document.querySelector<HTMLLinkElement>(selector);
return { href, link };
}
private listenToLanguageChanges(injector: Injector) {
const l10n = injector.get(LocalizationService);
// will always listen, no need to unsubscribe
l10n.languageChange
.pipe(
map(({ payload }) => payload),
startWith(l10n.currentLang),
)
.subscribe(locale => {
this.dir = getLocaleDirection(locale);
});
}
private setHtmlDir(dir: LocaleDirection) {
document.querySelector('html').dir = dir;
}
private setLazyLoad(injector: Injector) {
this.lazyLoad = injector.get(LazyLoadService);
const { href, link } = this.getLoadedBootstrap();
this.lazyLoad.loaded.set(href, link);
}
private setStyles(injector: Injector) {
this.styles = injector.get(LAZY_STYLES, [BOOTSTRAP]);
}
private switchCSS(dir: LocaleDirection) {
this.styles.forEach(style => {
const oldHref = createLazyStyleHref(style, this.dir);
const newHref = createLazyStyleHref(style, dir);
const strategy = LOADING_STRATEGY.PrependAnonymousStyleToHead(newHref);
this.lazyLoad.load(strategy).subscribe(() => this.lazyLoad.remove(oldHref));
});
}
}
export function createLazyStyleHref(style: string, dir: string): string {
return style.replace(/{{\s*dir\s*}}/g, dir);
}
export function initLazyStyleHandler(injector: Injector) {
return () => new LazyStyleHandler(injector);
}
interface LoadedStyle {
href: string;
link: HTMLLinkElement;
}

66
npm/ng-packs/packages/theme-shared/src/lib/tests/lazy-style.handler.spec.ts

@ -0,0 +1,66 @@
import { LazyLoadService, LOADING_STRATEGY, LocalizationService } from '@abp/ng.core';
import { createServiceFactory, SpectatorService } from '@ngneat/spectator/jest';
import { EMPTY, of } from 'rxjs';
import { BOOTSTRAP } from '../constants/styles';
import { createLazyStyleHref, initLazyStyleHandler, LazyStyleHandler } from '../handlers';
const languageChange = of({ payload: 'en' });
describe('LazyStyleHandler', () => {
let spectator: SpectatorService<LazyStyleHandler>;
let handler: LazyStyleHandler;
let lazyLoad: LazyLoadService;
const createService = createServiceFactory({
service: LazyStyleHandler,
providers: [
{
provide: LocalizationService,
useValue: { currentLang: 'en', languageChange },
},
],
});
beforeEach(() => {
spectator = createService();
handler = spectator.service;
lazyLoad = handler['lazyLoad'];
});
describe('#dir', () => {
it('should initially be "ltr"', () => {
expect(handler.dir).toBe('ltr');
});
it('should set bootstrap to rtl', () => {
const oldHref = createLazyStyleHref(BOOTSTRAP, 'ltr');
const newHref = createLazyStyleHref(BOOTSTRAP, 'rtl');
lazyLoad.loaded.set(newHref, null); // avoid actual loading
const load = jest.spyOn(lazyLoad, 'load');
const remove = jest.spyOn(lazyLoad, 'remove');
const strategy = LOADING_STRATEGY.PrependAnonymousStyleToHead(newHref);
handler.dir = 'rtl';
expect(load).toHaveBeenCalledWith(strategy);
expect(remove).toHaveBeenCalledWith(oldHref);
});
});
});
describe('initLazyStyleHandler', () => {
it('should return a LazyStyleHandler factory', () => {
const generator = (function*() {
yield undefined; // LAZY_STYLES
yield { loaded: new Map() }; // LazyLoadService
yield { currentLang: 'en', languageChange: EMPTY }; // LocalizationService
})();
const injector = {
get: () => generator.next().value as any,
};
const factory = initLazyStyleHandler(injector);
expect(factory()).toBeInstanceOf(LazyStyleHandler);
});
});
Loading…
Cancel
Save