mirror of https://github.com/abpframework/abp.git
3 changed files with 163 additions and 0 deletions
@ -1 +1,2 @@ |
|||||
export * from './error.handler'; |
export * from './error.handler'; |
||||
|
export * from './lazy-style.handler'; |
||||
|
|||||
@ -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; |
||||
|
} |
||||
@ -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…
Reference in new issue