fix: 重新合并 doc-viewer 中的翻译代码(未包含标题翻译)
This commit is contained in:
parent
0f8d7fd8ce
commit
c29aee6729
|
@ -1,4 +1,5 @@
|
||||||
import { Component, ComponentRef, DoCheck, ElementRef, EventEmitter, Input, OnDestroy, Output } from '@angular/core';
|
import { Component, ComponentRef, DoCheck, ElementRef, EventEmitter, Input, OnDestroy, Output } from '@angular/core';
|
||||||
|
import { HostListener } from '@angular/core';
|
||||||
import { Title, Meta } from '@angular/platform-browser';
|
import { Title, Meta } from '@angular/platform-browser';
|
||||||
|
|
||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable } from 'rxjs/Observable';
|
||||||
|
@ -69,25 +70,26 @@ export class DocViewerComponent implements DoCheck, OnDestroy {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
elementRef: ElementRef,
|
elementRef: ElementRef,
|
||||||
private embedComponentsService: EmbedComponentsService,
|
private embedComponentsService: EmbedComponentsService,
|
||||||
private logger: Logger,
|
private logger: Logger,
|
||||||
private titleService: Title,
|
private titleService: Title,
|
||||||
private metaService: Meta,
|
private metaService: Meta,
|
||||||
private tocService: TocService
|
private tocService: TocService
|
||||||
) {
|
) {
|
||||||
this.hostElement = elementRef.nativeElement;
|
this.hostElement = elementRef.nativeElement;
|
||||||
// Security: the initialDocViewerContent comes from the prerendered DOM and is considered to be secure
|
// Security: the initialDocViewerContent comes from the prerendered DOM and is considered to be secure
|
||||||
this.hostElement.innerHTML = initialDocViewerContent;
|
this.hostElement.innerHTML = initialDocViewerContent;
|
||||||
|
swapOriginAndResult(this.hostElement);
|
||||||
|
|
||||||
if ( this.hostElement.firstElementChild){
|
if (this.hostElement.firstElementChild) {
|
||||||
this.currViewContainer = this.hostElement.firstElementChild as HTMLElement;
|
this.currViewContainer = this.hostElement.firstElementChild as HTMLElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.onDestroy$.subscribe(() => this.destroyEmbeddedComponents());
|
this.onDestroy$.subscribe(() => this.destroyEmbeddedComponents());
|
||||||
this.docContents$
|
this.docContents$
|
||||||
.switchMap(newDoc => this.render(newDoc))
|
.switchMap(newDoc => this.render(newDoc))
|
||||||
.takeUntil(this.onDestroy$)
|
.takeUntil(this.onDestroy$)
|
||||||
.subscribe();
|
.subscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
ngDoCheck() {
|
ngDoCheck() {
|
||||||
|
@ -116,11 +118,11 @@ export class DocViewerComponent implements DoCheck, OnDestroy {
|
||||||
|
|
||||||
if (hasToc) {
|
if (hasToc) {
|
||||||
titleEl!.insertAdjacentHTML('afterend', '<aio-toc class="embedded"></aio-toc>');
|
titleEl!.insertAdjacentHTML('afterend', '<aio-toc class="embedded"></aio-toc>');
|
||||||
}
|
}
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
this.tocService.reset();
|
this.tocService.reset();
|
||||||
let title: string|null = '';
|
let title: string | null = '';
|
||||||
|
|
||||||
// Only create ToC for docs with an `<h1>` heading.
|
// Only create ToC for docs with an `<h1>` heading.
|
||||||
// If you don't want a ToC, add "no-toc" class to `<h1>`.
|
// If you don't want a ToC, add "no-toc" class to `<h1>`.
|
||||||
|
@ -145,23 +147,24 @@ export class DocViewerComponent implements DoCheck, OnDestroy {
|
||||||
this.setNoIndex(doc.id === FILE_NOT_FOUND_ID || doc.id === FETCHING_ERROR_ID);
|
this.setNoIndex(doc.id === FILE_NOT_FOUND_ID || doc.id === FETCHING_ERROR_ID);
|
||||||
|
|
||||||
return this.void$
|
return this.void$
|
||||||
// Security: `doc.contents` is always authored by the documentation team
|
// Security: `doc.contents` is always authored by the documentation team
|
||||||
// and is considered to be safe.
|
// and is considered to be safe.
|
||||||
.do(() => this.nextViewContainer.innerHTML = doc.contents || '')
|
.do(() => this.nextViewContainer.innerHTML = doc.contents || '')
|
||||||
.do(() => addTitleAndToc = this.prepareTitleAndToc(this.nextViewContainer, doc.id))
|
.do(() => swapOriginAndResult(this.nextViewContainer))
|
||||||
.switchMap(() => this.embedComponentsService.embedInto(this.nextViewContainer))
|
.do(() => addTitleAndToc = this.prepareTitleAndToc(this.nextViewContainer, doc.id))
|
||||||
.do(() => this.docReady.emit())
|
.switchMap(() => this.embedComponentsService.embedInto(this.nextViewContainer))
|
||||||
.do(() => this.destroyEmbeddedComponents())
|
.do(() => this.docReady.emit())
|
||||||
.do(componentRefs => this.embeddedComponentRefs = componentRefs)
|
.do(() => this.destroyEmbeddedComponents())
|
||||||
.switchMap(() => this.swapViews(addTitleAndToc))
|
.do(componentRefs => this.embeddedComponentRefs = componentRefs)
|
||||||
.do(() => this.docRendered.emit())
|
.switchMap(() => this.swapViews(addTitleAndToc))
|
||||||
.catch(err => {
|
.do(() => this.docRendered.emit())
|
||||||
const errorMessage = (err instanceof Error) ? err.stack : err;
|
.catch(err => {
|
||||||
this.logger.error(`[DocViewer] Error preparing document '${doc.id}': ${errorMessage}`);
|
const errorMessage = (err instanceof Error) ? err.stack : err;
|
||||||
this.nextViewContainer.innerHTML = '';
|
this.logger.error(`[DocViewer] Error preparing document '${doc.id}': ${errorMessage}`);
|
||||||
this.setNoIndex(true);
|
this.nextViewContainer.innerHTML = '';
|
||||||
return this.void$;
|
this.setNoIndex(true);
|
||||||
});
|
return this.void$;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -169,8 +172,8 @@ export class DocViewerComponent implements DoCheck, OnDestroy {
|
||||||
*/
|
*/
|
||||||
private setNoIndex(val: boolean) {
|
private setNoIndex(val: boolean) {
|
||||||
if (val) {
|
if (val) {
|
||||||
this.metaService.addTag({ name: 'googlebot', content: 'noindex' });
|
this.metaService.addTag({name: 'googlebot', content: 'noindex'});
|
||||||
this.metaService.addTag({ name: 'robots', content: 'noindex' });
|
this.metaService.addTag({name: 'robots', content: 'noindex'});
|
||||||
} else {
|
} else {
|
||||||
this.metaService.removeTag('name="googlebot"');
|
this.metaService.removeTag('name="googlebot"');
|
||||||
this.metaService.removeTag('name="robots"');
|
this.metaService.removeTag('name="robots"');
|
||||||
|
@ -204,26 +207,26 @@ export class DocViewerComponent implements DoCheck, OnDestroy {
|
||||||
return 1000 * seconds;
|
return 1000 * seconds;
|
||||||
};
|
};
|
||||||
const animateProp =
|
const animateProp =
|
||||||
(elem: HTMLElement, prop: keyof CSSStyleDeclaration, from: string, to: string, duration = 200) => {
|
(elem: HTMLElement, prop: keyof CSSStyleDeclaration, from: string, to: string, duration = 200) => {
|
||||||
const animationsDisabled = !DocViewerComponent.animationsEnabled
|
const animationsDisabled = !DocViewerComponent.animationsEnabled
|
||||||
|| this.hostElement.classList.contains(NO_ANIMATIONS);
|
|| this.hostElement.classList.contains(NO_ANIMATIONS);
|
||||||
if (prop === 'length' || prop === 'parentRule') {
|
if (prop === 'length' || prop === 'parentRule') {
|
||||||
// We cannot animate length or parentRule properties because they are readonly
|
// We cannot animate length or parentRule properties because they are readonly
|
||||||
return this.void$;
|
return this.void$;
|
||||||
}
|
}
|
||||||
elem.style.transition = '';
|
elem.style.transition = '';
|
||||||
return animationsDisabled
|
return animationsDisabled
|
||||||
? this.void$.do(() => elem.style[prop] = to)
|
? this.void$.do(() => elem.style[prop] = to)
|
||||||
: this.void$
|
: this.void$
|
||||||
// In order to ensure that the `from` value will be applied immediately (i.e.
|
// In order to ensure that the `from` value will be applied immediately (i.e.
|
||||||
// without transition) and that the `to` value will be affected by the
|
// without transition) and that the `to` value will be affected by the
|
||||||
// `transition` style, we need to ensure an animation frame has passed between
|
// `transition` style, we need to ensure an animation frame has passed between
|
||||||
// setting each style.
|
// setting each style.
|
||||||
.switchMap(() => raf$).do(() => elem.style[prop] = from)
|
.switchMap(() => raf$).do(() => elem.style[prop] = from)
|
||||||
.switchMap(() => raf$).do(() => elem.style.transition = `all ${duration}ms ease-in-out`)
|
.switchMap(() => raf$).do(() => elem.style.transition = `all ${duration}ms ease-in-out`)
|
||||||
.switchMap(() => raf$).do(() => (elem.style as any)[prop] = to)
|
.switchMap(() => raf$).do(() => (elem.style as any)[prop] = to)
|
||||||
.switchMap(() => timer(getActualDuration(elem))).switchMap(() => this.void$);
|
.switchMap(() => timer(getActualDuration(elem))).switchMap(() => this.void$);
|
||||||
};
|
};
|
||||||
|
|
||||||
const animateLeave = (elem: HTMLElement) => animateProp(elem, 'opacity', '1', '0.1');
|
const animateLeave = (elem: HTMLElement) => animateProp(elem, 'opacity', '1', '0.1');
|
||||||
const animateEnter = (elem: HTMLElement) => animateProp(elem, 'opacity', '0.1', '1');
|
const animateEnter = (elem: HTMLElement) => animateProp(elem, 'opacity', '0.1', '1');
|
||||||
|
@ -232,24 +235,60 @@ export class DocViewerComponent implements DoCheck, OnDestroy {
|
||||||
|
|
||||||
if (this.currViewContainer.parentElement) {
|
if (this.currViewContainer.parentElement) {
|
||||||
done$ = done$
|
done$ = done$
|
||||||
// Remove the current view from the viewer.
|
// Remove the current view from the viewer.
|
||||||
.switchMap(() => animateLeave(this.currViewContainer))
|
.switchMap(() => animateLeave(this.currViewContainer))
|
||||||
.do(() => this.currViewContainer.parentElement!.removeChild(this.currViewContainer))
|
.do(() => this.currViewContainer.parentElement!.removeChild(this.currViewContainer))
|
||||||
.do(() => this.docRemoved.emit());
|
.do(() => this.docRemoved.emit());
|
||||||
}
|
}
|
||||||
|
|
||||||
return done$
|
return done$
|
||||||
// Insert the next view into the viewer.
|
// Insert the next view into the viewer.
|
||||||
.do(() => this.hostElement.appendChild(this.nextViewContainer))
|
.do(() => this.hostElement.appendChild(this.nextViewContainer))
|
||||||
.do(() => onInsertedCb())
|
.do(() => onInsertedCb())
|
||||||
.do(() => this.docInserted.emit())
|
.do(() => this.docInserted.emit())
|
||||||
.switchMap(() => animateEnter(this.nextViewContainer))
|
.switchMap(() => animateEnter(this.nextViewContainer))
|
||||||
// Update the view references and clean up unused nodes.
|
// Update the view references and clean up unused nodes.
|
||||||
.do(() => {
|
.do(() => {
|
||||||
const prevViewContainer = this.currViewContainer;
|
const prevViewContainer = this.currViewContainer;
|
||||||
this.currViewContainer = this.nextViewContainer;
|
this.currViewContainer = this.nextViewContainer;
|
||||||
this.nextViewContainer = prevViewContainer;
|
this.nextViewContainer = prevViewContainer;
|
||||||
this.nextViewContainer.innerHTML = ''; // Empty to release memory.
|
this.nextViewContainer.innerHTML = ''; // Empty to release memory.
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@HostListener('click', ['$event'])
|
||||||
|
toggleTranslationOrigin($event: MouseEvent): void {
|
||||||
|
const element = findTranslationResult($event.target as Element);
|
||||||
|
if (element && element.hasAttribute('translation-result')) {
|
||||||
|
const origin = element.nextElementSibling;
|
||||||
|
if (!origin || origin.hasAttribute('translation-result') || origin.tagName !== element.tagName) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (origin.getAttribute('translation-origin') === 'on') {
|
||||||
|
origin.setAttribute('translation-origin', 'off');
|
||||||
|
} else {
|
||||||
|
origin.setAttribute('translation-origin', 'on');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function findTranslationResult(element: Element | null): Element | null {
|
||||||
|
while (element && !element.hasAttribute('translation-result')) {
|
||||||
|
element = element.parentElement;
|
||||||
|
}
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
function swapOriginAndResult(root: Element): void {
|
||||||
|
const results = root.querySelectorAll('[translation-result]');
|
||||||
|
for (let i = 0; i < results.length; ++i) {
|
||||||
|
const result = results.item(i);
|
||||||
|
const origin = result.previousElementSibling;
|
||||||
|
if (origin && origin.hasAttribute('translation-origin') && origin.parentElement) {
|
||||||
|
origin.parentElement.insertBefore(result, origin);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue