mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-17 02:31:34 +08:00
fix(ssr): fix global window and document references (#17590)
This commit is contained in:
@ -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%';
|
||||
|
||||
|
Reference in New Issue
Block a user