Skip to content
Permalink
Browse files

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

  • Loading branch information...
adamdbradley committed Feb 23, 2019
1 parent 6bea9d3 commit 4646f53ec7ab39a2e89f0c59a427b6b61ea7788e
@@ -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);
@@ -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(),
}
};
@@ -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 = '';
@@ -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) {
@@ -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);

@@ -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();
@@ -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 =
@@ -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 = () => {
@@ -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);

@@ -116,14 +116,17 @@ export class SplitPane implements ComponentInterface {
return;
}

// 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);
this.visible = mediaList.matches;
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 as any).addListener(callback as any);
this.rmL = () => (mediaList as any).removeListener(callback as any);
this.visible = mediaList.matches;
}
}

private isPane(element: HTMLElement): boolean {
@@ -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)) ||
@@ -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('='))
@@ -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);
}
@@ -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 {
@@ -14,7 +14,7 @@ export function getScrollData(componentEl: HTMLElement, contentEl: HTMLElement,
itemEl.getBoundingClientRect(),
contentEl.getBoundingClientRect(),
keyboardHeight,
window.innerHeight
(componentEl as any).ownerDocument.defaultView.innerHeight
);
}

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

0 comments on commit 4646f53

Please sign in to comment.
You can’t perform that action at this time.