fix(ssr): fix global window and document references (#17590)

This commit is contained in:
Adam Bradley
2019-02-22 20:13:09 -06:00
committed by GitHub
parent 6bea9d3248
commit 4646f53ec7
18 changed files with 76 additions and 70 deletions

View File

@ -120,7 +120,7 @@ export class Button implements ComponentInterface {
if (form) {
ev.preventDefault();
const fakeButton = document.createElement('button');
const fakeButton = this.win.document.createElement('button');
fakeButton.type = this.type;
fakeButton.style.display = 'none';
form.appendChild(fakeButton);

View File

@ -232,30 +232,25 @@ export class Col implements ComponentInterface {
};
}
private calculateOffset() {
const isRTL = document.dir === 'rtl';
private calculateOffset(isRTL: boolean) {
return this.calculatePosition('offset', isRTL ? 'margin-right' : 'margin-left');
}
private calculatePull() {
const isRTL = document.dir === 'rtl';
private calculatePull(isRTL: boolean) {
return this.calculatePosition('pull', isRTL ? 'left' : 'right');
}
private calculatePush() {
const isRTL = document.dir === 'rtl';
private calculatePush(isRTL: boolean) {
return this.calculatePosition('push', isRTL ? 'right' : 'left');
}
hostData() {
const isRTL = this.win.document.dir === 'rtl';
return {
style: {
...this.calculateOffset(),
...this.calculatePull(),
...this.calculatePush(),
...this.calculateOffset(isRTL),
...this.calculatePull(isRTL),
...this.calculatePush(isRTL),
...this.calculateSize(),
}
};

View File

@ -274,10 +274,10 @@ export class ItemSliding implements ComponentInterface {
? SlidingState.Start | SlidingState.SwipeStart
: SlidingState.Start;
} else {
this.tmr = window.setTimeout(() => {
this.tmr = setTimeout(() => {
this.state = SlidingState.Disabled;
this.tmr = undefined;
}, 600);
}, 600) as any;
openSlidingItem = undefined;
style.transform = '';

View File

@ -155,7 +155,7 @@ export class Menu implements ComponentInterface, MenuI {
const el = this.el;
const parent = el.parentNode as any;
const content = this.contentId !== undefined
? document.getElementById(this.contentId)
? this.doc.getElementById(this.contentId)
: parent && parent.querySelector && parent.querySelector('[main]');
if (!content || !content.tagName) {

View File

@ -15,7 +15,7 @@ export function iosLeaveAnimation(AnimationC: Animation, baseEl: HTMLElement): P
const wrapperElRect = wrapperEl!.getBoundingClientRect();
wrapperAnimation.beforeStyles({ 'opacity': 1 })
.fromTo('translateY', '0%', `${window.innerHeight - wrapperElRect.top}px`);
.fromTo('translateY', '0%', `${(baseEl.ownerDocument as any).defaultView.innerHeight - wrapperElRect.top}px`);
backdropAnimation.fromTo('opacity', 0.4, 0.0);

View File

@ -12,8 +12,8 @@ export function iosEnterAnimation(AnimationC: Animation, baseEl: HTMLElement, ev
const contentWidth = contentDimentions.width;
const contentHeight = contentDimentions.height;
const bodyWidth = window.innerWidth;
const bodyHeight = window.innerHeight;
const bodyWidth = (baseEl.ownerDocument as any).defaultView.innerWidth;
const bodyHeight = (baseEl.ownerDocument as any).defaultView.innerHeight;
// If ev was passed, use that for target element
const targetDim = ev && ev.target && (ev.target as HTMLElement).getBoundingClientRect();

View File

@ -4,7 +4,8 @@ import { Animation } from '../../../interface';
* Md Popover Enter Animation
*/
export function mdEnterAnimation(AnimationC: Animation, baseEl: HTMLElement, ev?: Event): Promise<Animation> {
const isRTL = document.dir === 'rtl';
const doc = (baseEl.ownerDocument as any);
const isRTL = doc.dir === 'rtl';
let originY = 'top';
let originX = isRTL ? 'right' : 'left';
@ -14,8 +15,8 @@ export function mdEnterAnimation(AnimationC: Animation, baseEl: HTMLElement, ev?
const contentWidth = contentDimentions.width;
const contentHeight = contentDimentions.height;
const bodyWidth = window.innerWidth;
const bodyHeight = window.innerHeight;
const bodyWidth = doc.defaultView.innerWidth;
const bodyHeight = doc.defaultView.innerHeight;
// If ev was passed, use that for target element
const targetDim =

View File

@ -27,6 +27,7 @@ export class Range implements ComponentInterface {
@Element() el!: HTMLStencilElement;
@Prop({ context: 'queue' }) queue!: QueueApi;
@Prop({ context: 'document' }) doc!: Document;
@State() private ratioA = 0;
@State() private ratioB = 0;
@ -240,7 +241,7 @@ export class Range implements ComponentInterface {
// figure out which knob they started closer to
let ratio = clamp(0, (currentX - rect.left) / rect.width, 1);
if (document.dir === 'rtl') {
if (this.doc.dir === 'rtl') {
ratio = 1 - ratio;
}
@ -270,7 +271,7 @@ export class Range implements ComponentInterface {
// update the knob being interacted with
const rect = this.rect;
let ratio = clamp(0, (currentX - rect.left) / rect.width, 1);
if (document.dir === 'rtl') {
if (this.doc.dir === 'rtl') {
ratio = 1 - ratio;
}
@ -368,7 +369,8 @@ export class Range implements ComponentInterface {
const barStart = `${ratioLower * 100}%`;
const barEnd = `${100 - ratioUpper * 100}%`;
const isRTL = document.dir === 'rtl';
const doc = this.doc;
const isRTL = doc.dir === 'rtl';
const start = isRTL ? 'right' : 'left';
const end = isRTL ? 'left' : 'right';
@ -426,7 +428,7 @@ export class Range implements ComponentInterface {
style={barStyle()}
/>
{ renderKnob({
{ renderKnob(isRTL, {
knob: 'A',
pressed: this.pressedKnob === 'A',
value: this.valA,
@ -438,7 +440,7 @@ export class Range implements ComponentInterface {
max
})}
{ this.dualKnobs && renderKnob({
{ this.dualKnobs && renderKnob(isRTL, {
knob: 'B',
pressed: this.pressedKnob === 'B',
value: this.valB,
@ -468,8 +470,7 @@ interface RangeKnob {
handleKeyboard: (name: KnobName, isIncrease: boolean) => void;
}
function renderKnob({ knob, value, ratio, min, max, disabled, pressed, pin, handleKeyboard }: RangeKnob) {
const isRTL = document.dir === 'rtl';
function renderKnob(isRTL: boolean, { knob, value, ratio, min, max, disabled, pressed, pin, handleKeyboard }: RangeKnob) {
const start = isRTL ? 'right' : 'left';
const knobStyle = () => {

View File

@ -92,7 +92,7 @@ export class Router implements ComponentInterface {
@Method()
push(url: string, direction: RouterDirection = 'forward') {
if (url.startsWith('.')) {
url = (new URL(url, window.location.href)).pathname;
url = (new URL(url, this.win.location.href)).pathname;
}
console.debug('[ion-router] URL pushed -> updating nav', url, direction);

View File

@ -116,15 +116,18 @@ export class SplitPane implements ComponentInterface {
return;
}
if ((this.win as any).matchMedia) {
// Listen on media query
const callback = (q: MediaQueryList) => {
this.visible = q.matches;
};
const mediaList = this.win.matchMedia(mediaQuery);
mediaList.addListener(callback as any);
this.rmL = () => mediaList.removeListener(callback as any);
(mediaList as any).addListener(callback as any);
this.rmL = () => (mediaList as any).removeListener(callback as any);
this.visible = mediaList.matches;
}
}
private isPane(element: HTMLElement): boolean {
if (!this.visible) {

View File

@ -24,6 +24,8 @@ export class Toggle implements ComponentInterface {
@Prop({ context: 'queue' }) queue!: QueueApi;
@Prop({ context: 'document' }) doc!: Document;
@State() activated = false;
/**
@ -146,7 +148,7 @@ export class Toggle implements ComponentInterface {
}
private onMove(detail: GestureDetail) {
if (shouldToggle(this.checked, detail.deltaX, -10)) {
if (shouldToggle(this.doc, this.checked, detail.deltaX, -10)) {
this.checked = !this.checked;
hapticSelection();
}
@ -222,8 +224,8 @@ export class Toggle implements ComponentInterface {
}
}
function shouldToggle(checked: boolean, deltaX: number, margin: number): boolean {
const isRTL = document.dir === 'rtl';
function shouldToggle(doc: HTMLDocument, checked: boolean, deltaX: number, margin: number): boolean {
const isRTL = doc.dir === 'rtl';
if (checked) {
return (!isRTL && (margin > deltaX)) ||

View File

@ -34,26 +34,25 @@ export class Config {
}
}
export function configFromSession(): any {
export function configFromSession(win: Window): any {
try {
const configStr = window.sessionStorage.getItem(IONIC_SESSION_KEY);
const configStr = win.sessionStorage.getItem(IONIC_SESSION_KEY);
return configStr !== null ? JSON.parse(configStr) : {};
} catch (e) {
return {};
}
}
export function saveConfig(config: any) {
export function saveConfig(win: Window, config: any) {
try {
window.sessionStorage.setItem(IONIC_SESSION_KEY, JSON.stringify(config));
win.sessionStorage.setItem(IONIC_SESSION_KEY, JSON.stringify(config));
} catch (e) {
return;
}
}
export function configFromURL() {
export function configFromURL(win: Window) {
const config: any = {};
const win = window;
win.location.search.slice(1)
.split('&')
.map(entry => entry.split('='))

View File

@ -4,10 +4,12 @@ import { isPlatform, setupPlatforms } from '../utils/platform';
import { Config, configFromSession, configFromURL, saveConfig } from './config';
const win = window;
const Ionic = (win as any)['Ionic'] = (win as any)['Ionic'] || {};
declare const Context: any;
const win = Context['window'] ? Context['window'] : typeof (window as any) !== 'undefined' ? window : {} as Window;
const Ionic = (win as any)['Ionic'] = (win as any)['Ionic'] || {};
// queue used to coordinate DOM reads and
// write in order to avoid layout thrashing
Object.defineProperty(Ionic, 'queue', {
@ -21,25 +23,27 @@ Context.isPlatform = isPlatform;
// create the Ionic.config from raw config object (if it exists)
// and convert Ionic.config into a ConfigApi that has a get() fn
const configObj = {
...configFromSession(),
...configFromSession(win),
persistConfig: false,
...Ionic['config'],
...configFromURL()
...configFromURL(win)
};
const config = Ionic['config'] = Context['config'] = new Config(configObj);
if (config.getBoolean('persistConfig')) {
saveConfig(configObj);
saveConfig(win, configObj);
}
// first see if the mode was set as an attribute on <html>
// which could have been set by the user, or by prerendering
// otherwise get the mode via config settings, and fallback to md
const documentElement = document.documentElement!;
const mode = config.get('mode', documentElement.getAttribute('mode') || (isPlatform(win, 'ios') ? 'ios' : 'md'));
const documentElement = win.document ? win.document.documentElement : null;
const mode = config.get('mode', (documentElement && documentElement.getAttribute('mode')) || (isPlatform(win, 'ios') ? 'ios' : 'md'));
Ionic.mode = Context.mode = mode;
config.set('mode', mode);
documentElement.setAttribute('mode', mode);
documentElement.classList.add(mode);
if (documentElement) {
documentElement.setAttribute('mode', mode);
documentElement.classList.add(mode);
}
if (config.getBoolean('_testing')) {
config.set('animated', false);
}

View File

@ -25,8 +25,9 @@ export const TRANSFORM_PROPS: {[key: string]: number} = {
'perspective': 1
};
const raf = (window as any).requestAnimationFrame
? window.requestAnimationFrame.bind(window)
const win = typeof (window as any) !== 'undefined' ? window : {};
const raf = (win as any).requestAnimationFrame
? (win as Window).requestAnimationFrame.bind(win)
: (f: FrameRequestCallback) => f(Date.now());
export class Animator {

View File

@ -14,7 +14,7 @@ export function getScrollData(componentEl: HTMLElement, contentEl: HTMLElement,
itemEl.getBoundingClientRect(),
contentEl.getBoundingClientRect(),
keyboardHeight,
window.innerHeight
(componentEl as any).ownerDocument.defaultView.innerHeight
);
}

View File

@ -14,6 +14,9 @@ export function matchBreakpoint(win: Window, breakpoint: string | undefined) {
if (breakpoint === undefined || breakpoint === '') {
return true;
}
if ((win as any).matchMedia) {
const mediaQuery = SIZE_TO_MEDIA[breakpoint];
return win.matchMedia(mediaQuery).matches;
}
return false;
}

View File

@ -32,8 +32,7 @@ export function setupPlatforms(win: any) {
let platforms: string[] | undefined | null = win.Ionic.platforms;
if (platforms == null) {
platforms = win.Ionic.platforms = detectPlatforms(win);
const classList = win.document.documentElement.classList;
platforms.forEach(p => classList.add(`plt-${p}`));
platforms.forEach(p => win.document.documentElement.classList.add(`plt-${p}`));
}
return platforms;
}
@ -93,13 +92,11 @@ function isHybrid(win: Window) {
return isCordova(win) || isCapacitorNative(win);
}
function isCordova(window: Window): boolean {
const win = window as any;
function isCordova(win: any): boolean {
return !!(win['cordova'] || win['phonegap'] || win['PhoneGap']);
}
function isCapacitorNative(window: Window): boolean {
const win = window as any;
function isCapacitorNative(win: any): boolean {
const capacitor = win['Capacitor'];
return !!(capacitor && capacitor.isNative);
}
@ -116,6 +113,6 @@ export function testUserAgent(win: Window, expr: RegExp) {
return expr.test(win.navigator.userAgent);
}
function matchMedia(win: Window, query: string): boolean {
return win.matchMedia(query).matches;
function matchMedia(win: any, query: string): boolean {
return win.matchMedia ? win.matchMedia(query).matches : false;
}

View File

@ -15,7 +15,7 @@ export function shadow<T extends Element>(el: T): ShadowRoot | T {
export function iosTransitionAnimation(AnimationC: Animation, navEl: HTMLElement, opts: TransitionOptions): Promise<Animation> {
const isRTL = document.dir === 'rtl';
const isRTL = (navEl.ownerDocument as any).dir === 'rtl';
const OFF_RIGHT = isRTL ? '-99.5%' : '99.5%';
const OFF_LEFT = isRTL ? '33%' : '-33%';