feature(react): rc2 release
* fix(): add a page with class ion-page back to ionrouteroutlet - fixes #19146 * wip * fix(react): attributes show up in dom * chore(): adding ion-page to core wip * wip * fix destroy method * wrap dom writes in raf * Add comments * fix(react): IonPage work * chore(): ionpage rc3 changelog text * fix(): syncing ion-page in a new way to get rid of timeout loop * chore(): ViewStacks refactor out of router * fix(): remove unused method in router * wip - before setActiveView rework * fix(): react router ion page work * chore(): cleanup and dev release * fix(): remove need to name tabs * chore(): adding dev mode helpers * fix(): adding className prop to back button fixes #19251 * fix(): routerDirection changes * chore(): rc2 release * fix(): fix react version in package * chores(): build kickoff
This commit is contained in:
parent
aec2936725
commit
73dd70d756
|
@ -765,15 +765,15 @@ function add(...args) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Swiper 4.5.0
|
||||
* Swiper 4.4.6
|
||||
* Most modern mobile touch slider and framework with hardware accelerated transitions
|
||||
* http://www.idangero.us/swiper/
|
||||
*
|
||||
* Copyright 2014-2019 Vladimir Kharlampidi
|
||||
* Copyright 2014-2018 Vladimir Kharlampidi
|
||||
*
|
||||
* Released under the MIT License
|
||||
*
|
||||
* Released on: February 22, 2019
|
||||
* Released on: December 19, 2018
|
||||
*/
|
||||
|
||||
const Methods = {
|
||||
|
@ -785,11 +785,11 @@ const Methods = {
|
|||
removeAttr,
|
||||
data,
|
||||
transform,
|
||||
transition: transition,
|
||||
transition,
|
||||
on,
|
||||
off,
|
||||
trigger,
|
||||
transitionEnd: transitionEnd,
|
||||
transitionEnd,
|
||||
outerWidth,
|
||||
outerHeight,
|
||||
offset,
|
||||
|
@ -935,7 +935,7 @@ const Support = (function Support() {
|
|||
return !!((win.navigator.maxTouchPoints > 0) || ('ontouchstart' in win) || (win.DocumentTouch && doc instanceof win.DocumentTouch));
|
||||
}()),
|
||||
|
||||
pointerEvents: !!(win.navigator.pointerEnabled || win.PointerEvent || ('maxTouchPoints' in win.navigator && win.navigator.maxTouchPoints > 0)),
|
||||
pointerEvents: !!(win.navigator.pointerEnabled || win.PointerEvent || ('maxTouchPoints' in win.navigator)),
|
||||
prefixedPointerEvents: !!win.navigator.msPointerEnabled,
|
||||
|
||||
transition: (function checkTransition() {
|
||||
|
@ -949,9 +949,9 @@ const Support = (function Support() {
|
|||
|
||||
flexbox: (function checkFlexbox() {
|
||||
const style = testDiv.style;
|
||||
const styles = ('alignItems webkitAlignItems webkitBoxAlign msFlexAlign mozBoxAlign webkitFlexDirection msFlexDirection mozBoxDirection mozBoxOrient webkitBoxDirection webkitBoxOrient').split(' ');
|
||||
for (let i = 0; i < styles.length; i += 1) {
|
||||
if (styles[i] in style) return true;
|
||||
const styles$$1 = ('alignItems webkitAlignItems webkitBoxAlign msFlexAlign mozBoxAlign webkitFlexDirection msFlexDirection mozBoxDirection mozBoxOrient webkitBoxDirection webkitBoxOrient').split(' ');
|
||||
for (let i = 0; i < styles$$1.length; i += 1) {
|
||||
if (styles$$1[i] in style) return true;
|
||||
}
|
||||
return false;
|
||||
}()),
|
||||
|
@ -982,19 +982,6 @@ const Support = (function Support() {
|
|||
};
|
||||
}());
|
||||
|
||||
const Browser = (function Browser() {
|
||||
function isSafari() {
|
||||
const ua = win.navigator.userAgent.toLowerCase();
|
||||
return (ua.indexOf('safari') >= 0 && ua.indexOf('chrome') < 0 && ua.indexOf('android') < 0);
|
||||
}
|
||||
return {
|
||||
isIE: !!win.navigator.userAgent.match(/Trident/g) || !!win.navigator.userAgent.match(/MSIE/g),
|
||||
isEdge: !!win.navigator.userAgent.match(/Edge/g),
|
||||
isSafari: isSafari(),
|
||||
isUiWebView: /(iPhone|iPod|iPad).*AppleWebKit(?!.*Safari)/i.test(win.navigator.userAgent),
|
||||
};
|
||||
}());
|
||||
|
||||
class SwiperClass {
|
||||
constructor(params = {}) {
|
||||
const self = this;
|
||||
|
@ -1027,11 +1014,7 @@ class SwiperClass {
|
|||
function onceHandler(...args) {
|
||||
handler.apply(self, args);
|
||||
self.off(events, onceHandler);
|
||||
if (onceHandler.f7proxy) {
|
||||
delete onceHandler.f7proxy;
|
||||
}
|
||||
}
|
||||
onceHandler.f7proxy = handler;
|
||||
return self.on(events, onceHandler, priority);
|
||||
}
|
||||
|
||||
|
@ -1042,9 +1025,9 @@ class SwiperClass {
|
|||
if (typeof handler === 'undefined') {
|
||||
self.eventsListeners[event] = [];
|
||||
} else if (self.eventsListeners[event] && self.eventsListeners[event].length) {
|
||||
self.eventsListeners[event].forEach((eventHandler, index) => {
|
||||
if (eventHandler === handler || (eventHandler.f7proxy && eventHandler.f7proxy === handler)) {
|
||||
self.eventsListeners[event].splice(index, 1);
|
||||
self.eventsListeners[event].forEach((eventHandler, index$$1) => {
|
||||
if (eventHandler === handler) {
|
||||
self.eventsListeners[event].splice(index$$1, 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -1056,15 +1039,15 @@ class SwiperClass {
|
|||
const self = this;
|
||||
if (!self.eventsListeners) return self;
|
||||
let events;
|
||||
let data;
|
||||
let data$$1;
|
||||
let context;
|
||||
if (typeof args[0] === 'string' || Array.isArray(args[0])) {
|
||||
events = args[0];
|
||||
data = args.slice(1, args.length);
|
||||
data$$1 = args.slice(1, args.length);
|
||||
context = self;
|
||||
} else {
|
||||
events = args[0].events;
|
||||
data = args[0].data;
|
||||
data$$1 = args[0].data;
|
||||
context = args[0].context || self;
|
||||
}
|
||||
const eventsArray = Array.isArray(events) ? events : events.split(' ');
|
||||
|
@ -1075,7 +1058,7 @@ class SwiperClass {
|
|||
handlers.push(eventHandler);
|
||||
});
|
||||
handlers.forEach((eventHandler) => {
|
||||
eventHandler.apply(context, data);
|
||||
eventHandler.apply(context, data$$1);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -1226,7 +1209,7 @@ function updateSlides () {
|
|||
let spaceBetween = params.spaceBetween;
|
||||
let slidePosition = -offsetBefore;
|
||||
let prevSlideSize = 0;
|
||||
let index = 0;
|
||||
let index$$1 = 0;
|
||||
if (typeof swiperSize === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
@ -1371,11 +1354,11 @@ function updateSlides () {
|
|||
if (i === 0) slidePosition = slidePosition - (swiperSize / 2) - spaceBetween;
|
||||
if (Math.abs(slidePosition) < 1 / 1000) slidePosition = 0;
|
||||
if (params.roundLengths) slidePosition = Math.floor(slidePosition);
|
||||
if ((index) % params.slidesPerGroup === 0) snapGrid.push(slidePosition);
|
||||
if ((index$$1) % params.slidesPerGroup === 0) snapGrid.push(slidePosition);
|
||||
slidesGrid.push(slidePosition);
|
||||
} else {
|
||||
if (params.roundLengths) slidePosition = Math.floor(slidePosition);
|
||||
if ((index) % params.slidesPerGroup === 0) snapGrid.push(slidePosition);
|
||||
if ((index$$1) % params.slidesPerGroup === 0) snapGrid.push(slidePosition);
|
||||
slidesGrid.push(slidePosition);
|
||||
slidePosition = slidePosition + slideSize + spaceBetween;
|
||||
}
|
||||
|
@ -1384,7 +1367,7 @@ function updateSlides () {
|
|||
|
||||
prevSlideSize = slideSize;
|
||||
|
||||
index += 1;
|
||||
index$$1 += 1;
|
||||
}
|
||||
swiper.virtualSize = Math.max(swiper.virtualSize, swiperSize) + offsetAfter;
|
||||
let newSlidesGrid;
|
||||
|
@ -1491,9 +1474,9 @@ function updateAutoHeight (speed) {
|
|||
// Find slides currently in view
|
||||
if (swiper.params.slidesPerView !== 'auto' && swiper.params.slidesPerView > 1) {
|
||||
for (i = 0; i < Math.ceil(swiper.params.slidesPerView); i += 1) {
|
||||
const index = swiper.activeIndex + i;
|
||||
if (index > swiper.slides.length) break;
|
||||
activeSlides.push(swiper.slides.eq(index)[0]);
|
||||
const index$$1 = swiper.activeIndex + i;
|
||||
if (index$$1 > swiper.slides.length) break;
|
||||
activeSlides.push(swiper.slides.eq(index$$1)[0]);
|
||||
}
|
||||
} else {
|
||||
activeSlides.push(swiper.slides.eq(swiper.activeIndex)[0]);
|
||||
|
@ -1907,9 +1890,9 @@ var transition$1 = {
|
|||
transitionEnd: transitionEnd$1,
|
||||
};
|
||||
|
||||
function slideTo (index = 0, speed = this.params.speed, runCallbacks = true, internal) {
|
||||
function slideTo (index$$1 = 0, speed = this.params.speed, runCallbacks = true, internal) {
|
||||
const swiper = this;
|
||||
let slideIndex = index;
|
||||
let slideIndex = index$$1;
|
||||
if (slideIndex < 0) slideIndex = 0;
|
||||
|
||||
const {
|
||||
|
@ -1991,7 +1974,7 @@ function slideTo (index = 0, speed = this.params.speed, runCallbacks = true, int
|
|||
if (!swiper.animating) {
|
||||
swiper.animating = true;
|
||||
if (!swiper.onSlideToWrapperTransitionEnd) {
|
||||
swiper.onSlideToWrapperTransitionEnd = function transitionEnd(e) {
|
||||
swiper.onSlideToWrapperTransitionEnd = function transitionEnd$$1(e) {
|
||||
if (!swiper || swiper.destroyed) return;
|
||||
if (e.target !== this) return;
|
||||
swiper.$wrapperEl[0].removeEventListener('transitionend', swiper.onSlideToWrapperTransitionEnd);
|
||||
|
@ -2009,9 +1992,9 @@ function slideTo (index = 0, speed = this.params.speed, runCallbacks = true, int
|
|||
return true;
|
||||
}
|
||||
|
||||
function slideToLoop (index = 0, speed = this.params.speed, runCallbacks = true, internal) {
|
||||
function slideToLoop (index$$1 = 0, speed = this.params.speed, runCallbacks = true, internal) {
|
||||
const swiper = this;
|
||||
let newIndex = index;
|
||||
let newIndex = index$$1;
|
||||
if (swiper.params.loop) {
|
||||
newIndex += swiper.loopedSlides;
|
||||
}
|
||||
|
@ -2074,8 +2057,8 @@ function slideReset (speed = this.params.speed, runCallbacks = true, internal) {
|
|||
/* eslint no-unused-vars: "off" */
|
||||
function slideToClosest (speed = this.params.speed, runCallbacks = true, internal) {
|
||||
const swiper = this;
|
||||
let index = swiper.activeIndex;
|
||||
const snapIndex = Math.floor(index / swiper.params.slidesPerGroup);
|
||||
let index$$1 = swiper.activeIndex;
|
||||
const snapIndex = Math.floor(index$$1 / swiper.params.slidesPerGroup);
|
||||
|
||||
if (snapIndex < swiper.snapGrid.length - 1) {
|
||||
const translate = swiper.rtlTranslate ? swiper.translate : -swiper.translate;
|
||||
|
@ -2084,11 +2067,11 @@ function slideToClosest (speed = this.params.speed, runCallbacks = true, interna
|
|||
const nextSnap = swiper.snapGrid[snapIndex + 1];
|
||||
|
||||
if ((translate - currentSnap) > (nextSnap - currentSnap) / 2) {
|
||||
index = swiper.params.slidesPerGroup;
|
||||
index$$1 = swiper.params.slidesPerGroup;
|
||||
}
|
||||
}
|
||||
|
||||
return swiper.slideTo(index, speed, runCallbacks, internal);
|
||||
return swiper.slideTo(index$$1, speed, runCallbacks, internal);
|
||||
}
|
||||
|
||||
function slideToClickedSlide () {
|
||||
|
@ -2175,11 +2158,11 @@ function loopCreate () {
|
|||
|
||||
const prependSlides = [];
|
||||
const appendSlides = [];
|
||||
slides.each((index, el) => {
|
||||
slides.each((index$$1, el) => {
|
||||
const slide = $(el);
|
||||
if (index < swiper.loopedSlides) appendSlides.push(el);
|
||||
if (index < slides.length && index >= slides.length - swiper.loopedSlides) prependSlides.push(el);
|
||||
slide.attr('data-swiper-slide-index', index);
|
||||
if (index$$1 < swiper.loopedSlides) appendSlides.push(el);
|
||||
if (index$$1 < slides.length && index$$1 >= slides.length - swiper.loopedSlides) prependSlides.push(el);
|
||||
slide.attr('data-swiper-slide-index', index$$1);
|
||||
});
|
||||
for (let i = 0; i < appendSlides.length; i += 1) {
|
||||
$wrapperEl.append($(appendSlides[i].cloneNode(true)).addClass(params.slideDuplicateClass));
|
||||
|
@ -2303,7 +2286,7 @@ function prependSlide (slides) {
|
|||
swiper.slideTo(newActiveIndex, 0, false);
|
||||
}
|
||||
|
||||
function addSlide (index, slides) {
|
||||
function addSlide (index$$1, slides) {
|
||||
const swiper = this;
|
||||
const { $wrapperEl, params, activeIndex } = swiper;
|
||||
let activeIndexBuffer = activeIndex;
|
||||
|
@ -2313,18 +2296,18 @@ function addSlide (index, slides) {
|
|||
swiper.slides = $wrapperEl.children(`.${params.slideClass}`);
|
||||
}
|
||||
const baseLength = swiper.slides.length;
|
||||
if (index <= 0) {
|
||||
if (index$$1 <= 0) {
|
||||
swiper.prependSlide(slides);
|
||||
return;
|
||||
}
|
||||
if (index >= baseLength) {
|
||||
if (index$$1 >= baseLength) {
|
||||
swiper.appendSlide(slides);
|
||||
return;
|
||||
}
|
||||
let newActiveIndex = activeIndexBuffer > index ? activeIndexBuffer + 1 : activeIndexBuffer;
|
||||
let newActiveIndex = activeIndexBuffer > index$$1 ? activeIndexBuffer + 1 : activeIndexBuffer;
|
||||
|
||||
const slidesBuffer = [];
|
||||
for (let i = baseLength - 1; i >= index; i -= 1) {
|
||||
for (let i = baseLength - 1; i >= index$$1; i -= 1) {
|
||||
const currentSlide = swiper.slides.eq(i);
|
||||
currentSlide.remove();
|
||||
slidesBuffer.unshift(currentSlide);
|
||||
|
@ -2334,7 +2317,7 @@ function addSlide (index, slides) {
|
|||
for (let i = 0; i < slides.length; i += 1) {
|
||||
if (slides[i]) $wrapperEl.append(slides[i]);
|
||||
}
|
||||
newActiveIndex = activeIndexBuffer > index ? activeIndexBuffer + slides.length : activeIndexBuffer;
|
||||
newActiveIndex = activeIndexBuffer > index$$1 ? activeIndexBuffer + slides.length : activeIndexBuffer;
|
||||
} else {
|
||||
$wrapperEl.append(slides);
|
||||
}
|
||||
|
@ -2500,17 +2483,17 @@ const Device = (function Device() {
|
|||
|
||||
function onTouchStart (event) {
|
||||
const swiper = this;
|
||||
const data = swiper.touchEventsData;
|
||||
const data$$1 = swiper.touchEventsData;
|
||||
const { params, touches } = swiper;
|
||||
if (swiper.animating && params.preventInteractionOnTransition) {
|
||||
return;
|
||||
}
|
||||
let e = event;
|
||||
if (e.originalEvent) e = e.originalEvent;
|
||||
data.isTouchEvent = e.type === 'touchstart';
|
||||
if (!data.isTouchEvent && 'which' in e && e.which === 3) return;
|
||||
if (!data.isTouchEvent && 'button' in e && e.button > 0) return;
|
||||
if (data.isTouched && data.isMoved) return;
|
||||
data$$1.isTouchEvent = e.type === 'touchstart';
|
||||
if (!data$$1.isTouchEvent && 'which' in e && e.which === 3) return;
|
||||
if (!data$$1.isTouchEvent && 'button' in e && e.button > 0) return;
|
||||
if (data$$1.isTouched && data$$1.isMoved) return;
|
||||
if (params.noSwiping && $(e.target).closest(params.noSwipingSelector ? params.noSwipingSelector : `.${params.noSwipingClass}`)[0]) {
|
||||
swiper.allowClick = true;
|
||||
return;
|
||||
|
@ -2536,7 +2519,7 @@ function onTouchStart (event) {
|
|||
return;
|
||||
}
|
||||
|
||||
Utils.extend(data, {
|
||||
Utils.extend(data$$1, {
|
||||
isTouched: true,
|
||||
isMoved: false,
|
||||
allowTouchCallbacks: true,
|
||||
|
@ -2546,17 +2529,17 @@ function onTouchStart (event) {
|
|||
|
||||
touches.startX = startX;
|
||||
touches.startY = startY;
|
||||
data.touchStartTime = Utils.now();
|
||||
data$$1.touchStartTime = Utils.now();
|
||||
swiper.allowClick = true;
|
||||
swiper.updateSize();
|
||||
swiper.swipeDirection = undefined;
|
||||
if (params.threshold > 0) data.allowThresholdMove = false;
|
||||
if (params.threshold > 0) data$$1.allowThresholdMove = false;
|
||||
if (e.type !== 'touchstart') {
|
||||
let preventDefault = true;
|
||||
if ($(e.target).is(data.formElements)) preventDefault = false;
|
||||
if ($(e.target).is(data$$1.formElements)) preventDefault = false;
|
||||
if (
|
||||
doc.activeElement
|
||||
&& $(doc.activeElement).is(data.formElements)
|
||||
&& $(doc.activeElement).is(data$$1.formElements)
|
||||
&& doc.activeElement !== e.target
|
||||
) {
|
||||
doc.activeElement.blur();
|
||||
|
@ -2572,17 +2555,17 @@ function onTouchStart (event) {
|
|||
|
||||
function onTouchMove (event) {
|
||||
const swiper = this;
|
||||
const data = swiper.touchEventsData;
|
||||
const data$$1 = swiper.touchEventsData;
|
||||
const { params, touches, rtlTranslate: rtl } = swiper;
|
||||
let e = event;
|
||||
if (e.originalEvent) e = e.originalEvent;
|
||||
if (!data.isTouched) {
|
||||
if (data.startMoving && data.isScrolling) {
|
||||
if (!data$$1.isTouched) {
|
||||
if (data$$1.startMoving && data$$1.isScrolling) {
|
||||
swiper.emit('touchMoveOpposite', e);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (data.isTouchEvent && e.type === 'mousemove') return;
|
||||
if (data$$1.isTouchEvent && e.type === 'mousemove') return;
|
||||
const pageX = e.type === 'touchmove' ? e.targetTouches[0].pageX : e.pageX;
|
||||
const pageY = e.type === 'touchmove' ? e.targetTouches[0].pageY : e.pageY;
|
||||
if (e.preventedByNestedSwiper) {
|
||||
|
@ -2593,26 +2576,26 @@ function onTouchMove (event) {
|
|||
if (!swiper.allowTouchMove) {
|
||||
// isMoved = true;
|
||||
swiper.allowClick = false;
|
||||
if (data.isTouched) {
|
||||
if (data$$1.isTouched) {
|
||||
Utils.extend(touches, {
|
||||
startX: pageX,
|
||||
startY: pageY,
|
||||
currentX: pageX,
|
||||
currentY: pageY,
|
||||
});
|
||||
data.touchStartTime = Utils.now();
|
||||
data$$1.touchStartTime = Utils.now();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (data.isTouchEvent && params.touchReleaseOnEdges && !params.loop) {
|
||||
if (data$$1.isTouchEvent && params.touchReleaseOnEdges && !params.loop) {
|
||||
if (swiper.isVertical()) {
|
||||
// Vertical
|
||||
if (
|
||||
(pageY < touches.startY && swiper.translate <= swiper.maxTranslate())
|
||||
|| (pageY > touches.startY && swiper.translate >= swiper.minTranslate())
|
||||
) {
|
||||
data.isTouched = false;
|
||||
data.isMoved = false;
|
||||
data$$1.isTouched = false;
|
||||
data$$1.isMoved = false;
|
||||
return;
|
||||
}
|
||||
} else if (
|
||||
|
@ -2622,14 +2605,14 @@ function onTouchMove (event) {
|
|||
return;
|
||||
}
|
||||
}
|
||||
if (data.isTouchEvent && doc.activeElement) {
|
||||
if (e.target === doc.activeElement && $(e.target).is(data.formElements)) {
|
||||
data.isMoved = true;
|
||||
if (data$$1.isTouchEvent && doc.activeElement) {
|
||||
if (e.target === doc.activeElement && $(e.target).is(data$$1.formElements)) {
|
||||
data$$1.isMoved = true;
|
||||
swiper.allowClick = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (data.allowTouchCallbacks) {
|
||||
if (data$$1.allowTouchCallbacks) {
|
||||
swiper.emit('touchMove', e);
|
||||
}
|
||||
if (e.targetTouches && e.targetTouches.length > 1) return;
|
||||
|
@ -2641,31 +2624,31 @@ function onTouchMove (event) {
|
|||
const diffY = touches.currentY - touches.startY;
|
||||
if (swiper.params.threshold && Math.sqrt((diffX ** 2) + (diffY ** 2)) < swiper.params.threshold) return;
|
||||
|
||||
if (typeof data.isScrolling === 'undefined') {
|
||||
if (typeof data$$1.isScrolling === 'undefined') {
|
||||
let touchAngle;
|
||||
if ((swiper.isHorizontal() && touches.currentY === touches.startY) || (swiper.isVertical() && touches.currentX === touches.startX)) {
|
||||
data.isScrolling = false;
|
||||
data$$1.isScrolling = false;
|
||||
} else {
|
||||
// eslint-disable-next-line
|
||||
if ((diffX * diffX) + (diffY * diffY) >= 25) {
|
||||
touchAngle = (Math.atan2(Math.abs(diffY), Math.abs(diffX)) * 180) / Math.PI;
|
||||
data.isScrolling = swiper.isHorizontal() ? touchAngle > params.touchAngle : (90 - touchAngle > params.touchAngle);
|
||||
data$$1.isScrolling = swiper.isHorizontal() ? touchAngle > params.touchAngle : (90 - touchAngle > params.touchAngle);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (data.isScrolling) {
|
||||
if (data$$1.isScrolling) {
|
||||
swiper.emit('touchMoveOpposite', e);
|
||||
}
|
||||
if (typeof data.startMoving === 'undefined') {
|
||||
if (typeof data$$1.startMoving === 'undefined') {
|
||||
if (touches.currentX !== touches.startX || touches.currentY !== touches.startY) {
|
||||
data.startMoving = true;
|
||||
data$$1.startMoving = true;
|
||||
}
|
||||
}
|
||||
if (data.isScrolling) {
|
||||
data.isTouched = false;
|
||||
if (data$$1.isScrolling) {
|
||||
data$$1.isTouched = false;
|
||||
return;
|
||||
}
|
||||
if (!data.startMoving) {
|
||||
if (!data$$1.startMoving) {
|
||||
return;
|
||||
}
|
||||
swiper.allowClick = false;
|
||||
|
@ -2674,16 +2657,16 @@ function onTouchMove (event) {
|
|||
e.stopPropagation();
|
||||
}
|
||||
|
||||
if (!data.isMoved) {
|
||||
if (!data$$1.isMoved) {
|
||||
if (params.loop) {
|
||||
swiper.loopFix();
|
||||
}
|
||||
data.startTranslate = swiper.getTranslate();
|
||||
data$$1.startTranslate = swiper.getTranslate();
|
||||
swiper.setTransition(0);
|
||||
if (swiper.animating) {
|
||||
swiper.$wrapperEl.trigger('webkitTransitionEnd transitionend');
|
||||
}
|
||||
data.allowMomentumBounce = false;
|
||||
data$$1.allowMomentumBounce = false;
|
||||
// Grab Cursor
|
||||
if (params.grabCursor && (swiper.allowSlideNext === true || swiper.allowSlidePrev === true)) {
|
||||
swiper.setGrabCursor(true);
|
||||
|
@ -2691,7 +2674,7 @@ function onTouchMove (event) {
|
|||
swiper.emit('sliderFirstMove', e);
|
||||
}
|
||||
swiper.emit('sliderMove', e);
|
||||
data.isMoved = true;
|
||||
data$$1.isMoved = true;
|
||||
|
||||
let diff = swiper.isHorizontal() ? diffX : diffY;
|
||||
touches.diff = diff;
|
||||
|
@ -2700,19 +2683,19 @@ function onTouchMove (event) {
|
|||
if (rtl) diff = -diff;
|
||||
|
||||
swiper.swipeDirection = diff > 0 ? 'prev' : 'next';
|
||||
data.currentTranslate = diff + data.startTranslate;
|
||||
data$$1.currentTranslate = diff + data$$1.startTranslate;
|
||||
|
||||
let disableParentSwiper = true;
|
||||
let resistanceRatio = params.resistanceRatio;
|
||||
if (params.touchReleaseOnEdges) {
|
||||
resistanceRatio = 0;
|
||||
}
|
||||
if ((diff > 0 && data.currentTranslate > swiper.minTranslate())) {
|
||||
if ((diff > 0 && data$$1.currentTranslate > swiper.minTranslate())) {
|
||||
disableParentSwiper = false;
|
||||
if (params.resistance) data.currentTranslate = (swiper.minTranslate() - 1) + ((-swiper.minTranslate() + data.startTranslate + diff) ** resistanceRatio);
|
||||
} else if (diff < 0 && data.currentTranslate < swiper.maxTranslate()) {
|
||||
if (params.resistance) data$$1.currentTranslate = (swiper.minTranslate() - 1) + ((-swiper.minTranslate() + data$$1.startTranslate + diff) ** resistanceRatio);
|
||||
} else if (diff < 0 && data$$1.currentTranslate < swiper.maxTranslate()) {
|
||||
disableParentSwiper = false;
|
||||
if (params.resistance) data.currentTranslate = (swiper.maxTranslate() + 1) - ((swiper.maxTranslate() - data.startTranslate - diff) ** resistanceRatio);
|
||||
if (params.resistance) data$$1.currentTranslate = (swiper.maxTranslate() + 1) - ((swiper.maxTranslate() - data$$1.startTranslate - diff) ** resistanceRatio);
|
||||
}
|
||||
|
||||
if (disableParentSwiper) {
|
||||
|
@ -2720,27 +2703,27 @@ function onTouchMove (event) {
|
|||
}
|
||||
|
||||
// Directions locks
|
||||
if (!swiper.allowSlideNext && swiper.swipeDirection === 'next' && data.currentTranslate < data.startTranslate) {
|
||||
data.currentTranslate = data.startTranslate;
|
||||
if (!swiper.allowSlideNext && swiper.swipeDirection === 'next' && data$$1.currentTranslate < data$$1.startTranslate) {
|
||||
data$$1.currentTranslate = data$$1.startTranslate;
|
||||
}
|
||||
if (!swiper.allowSlidePrev && swiper.swipeDirection === 'prev' && data.currentTranslate > data.startTranslate) {
|
||||
data.currentTranslate = data.startTranslate;
|
||||
if (!swiper.allowSlidePrev && swiper.swipeDirection === 'prev' && data$$1.currentTranslate > data$$1.startTranslate) {
|
||||
data$$1.currentTranslate = data$$1.startTranslate;
|
||||
}
|
||||
|
||||
|
||||
// Threshold
|
||||
if (params.threshold > 0) {
|
||||
if (Math.abs(diff) > params.threshold || data.allowThresholdMove) {
|
||||
if (!data.allowThresholdMove) {
|
||||
data.allowThresholdMove = true;
|
||||
if (Math.abs(diff) > params.threshold || data$$1.allowThresholdMove) {
|
||||
if (!data$$1.allowThresholdMove) {
|
||||
data$$1.allowThresholdMove = true;
|
||||
touches.startX = touches.currentX;
|
||||
touches.startY = touches.currentY;
|
||||
data.currentTranslate = data.startTranslate;
|
||||
data$$1.currentTranslate = data$$1.startTranslate;
|
||||
touches.diff = swiper.isHorizontal() ? touches.currentX - touches.startX : touches.currentY - touches.startY;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
data.currentTranslate = data.startTranslate;
|
||||
data$$1.currentTranslate = data$$1.startTranslate;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -2754,90 +2737,90 @@ function onTouchMove (event) {
|
|||
}
|
||||
if (params.freeMode) {
|
||||
// Velocity
|
||||
if (data.velocities.length === 0) {
|
||||
data.velocities.push({
|
||||
if (data$$1.velocities.length === 0) {
|
||||
data$$1.velocities.push({
|
||||
position: touches[swiper.isHorizontal() ? 'startX' : 'startY'],
|
||||
time: data.touchStartTime,
|
||||
time: data$$1.touchStartTime,
|
||||
});
|
||||
}
|
||||
data.velocities.push({
|
||||
data$$1.velocities.push({
|
||||
position: touches[swiper.isHorizontal() ? 'currentX' : 'currentY'],
|
||||
time: Utils.now(),
|
||||
});
|
||||
}
|
||||
// Update progress
|
||||
swiper.updateProgress(data.currentTranslate);
|
||||
swiper.updateProgress(data$$1.currentTranslate);
|
||||
// Update translate
|
||||
swiper.setTranslate(data.currentTranslate);
|
||||
swiper.setTranslate(data$$1.currentTranslate);
|
||||
}
|
||||
|
||||
function onTouchEnd (event) {
|
||||
const swiper = this;
|
||||
const data = swiper.touchEventsData;
|
||||
const data$$1 = swiper.touchEventsData;
|
||||
|
||||
const {
|
||||
params, touches, rtlTranslate: rtl, $wrapperEl, slidesGrid, snapGrid,
|
||||
} = swiper;
|
||||
let e = event;
|
||||
if (e.originalEvent) e = e.originalEvent;
|
||||
if (data.allowTouchCallbacks) {
|
||||
if (data$$1.allowTouchCallbacks) {
|
||||
swiper.emit('touchEnd', e);
|
||||
}
|
||||
data.allowTouchCallbacks = false;
|
||||
if (!data.isTouched) {
|
||||
if (data.isMoved && params.grabCursor) {
|
||||
data$$1.allowTouchCallbacks = false;
|
||||
if (!data$$1.isTouched) {
|
||||
if (data$$1.isMoved && params.grabCursor) {
|
||||
swiper.setGrabCursor(false);
|
||||
}
|
||||
data.isMoved = false;
|
||||
data.startMoving = false;
|
||||
data$$1.isMoved = false;
|
||||
data$$1.startMoving = false;
|
||||
return;
|
||||
}
|
||||
// Return Grab Cursor
|
||||
if (params.grabCursor && data.isMoved && data.isTouched && (swiper.allowSlideNext === true || swiper.allowSlidePrev === true)) {
|
||||
if (params.grabCursor && data$$1.isMoved && data$$1.isTouched && (swiper.allowSlideNext === true || swiper.allowSlidePrev === true)) {
|
||||
swiper.setGrabCursor(false);
|
||||
}
|
||||
|
||||
// Time diff
|
||||
const touchEndTime = Utils.now();
|
||||
const timeDiff = touchEndTime - data.touchStartTime;
|
||||
const timeDiff = touchEndTime - data$$1.touchStartTime;
|
||||
|
||||
// Tap, doubleTap, Click
|
||||
if (swiper.allowClick) {
|
||||
swiper.updateClickedSlide(e);
|
||||
swiper.emit('tap', e);
|
||||
if (timeDiff < 300 && (touchEndTime - data.lastClickTime) > 300) {
|
||||
if (data.clickTimeout) clearTimeout(data.clickTimeout);
|
||||
data.clickTimeout = Utils.nextTick(() => {
|
||||
if (timeDiff < 300 && (touchEndTime - data$$1.lastClickTime) > 300) {
|
||||
if (data$$1.clickTimeout) clearTimeout(data$$1.clickTimeout);
|
||||
data$$1.clickTimeout = Utils.nextTick(() => {
|
||||
if (!swiper || swiper.destroyed) return;
|
||||
swiper.emit('click', e);
|
||||
}, 300);
|
||||
}
|
||||
if (timeDiff < 300 && (touchEndTime - data.lastClickTime) < 300) {
|
||||
if (data.clickTimeout) clearTimeout(data.clickTimeout);
|
||||
if (timeDiff < 300 && (touchEndTime - data$$1.lastClickTime) < 300) {
|
||||
if (data$$1.clickTimeout) clearTimeout(data$$1.clickTimeout);
|
||||
swiper.emit('doubleTap', e);
|
||||
}
|
||||
}
|
||||
|
||||
data.lastClickTime = Utils.now();
|
||||
data$$1.lastClickTime = Utils.now();
|
||||
Utils.nextTick(() => {
|
||||
if (!swiper.destroyed) swiper.allowClick = true;
|
||||
});
|
||||
|
||||
if (!data.isTouched || !data.isMoved || !swiper.swipeDirection || touches.diff === 0 || data.currentTranslate === data.startTranslate) {
|
||||
data.isTouched = false;
|
||||
data.isMoved = false;
|
||||
data.startMoving = false;
|
||||
if (!data$$1.isTouched || !data$$1.isMoved || !swiper.swipeDirection || touches.diff === 0 || data$$1.currentTranslate === data$$1.startTranslate) {
|
||||
data$$1.isTouched = false;
|
||||
data$$1.isMoved = false;
|
||||
data$$1.startMoving = false;
|
||||
return;
|
||||
}
|
||||
data.isTouched = false;
|
||||
data.isMoved = false;
|
||||
data.startMoving = false;
|
||||
data$$1.isTouched = false;
|
||||
data$$1.isMoved = false;
|
||||
data$$1.startMoving = false;
|
||||
|
||||
let currentPos;
|
||||
if (params.followFinger) {
|
||||
currentPos = rtl ? swiper.translate : -swiper.translate;
|
||||
} else {
|
||||
currentPos = -data.currentTranslate;
|
||||
currentPos = -data$$1.currentTranslate;
|
||||
}
|
||||
|
||||
if (params.freeMode) {
|
||||
|
@ -2855,9 +2838,9 @@ function onTouchEnd (event) {
|
|||
}
|
||||
|
||||
if (params.freeModeMomentum) {
|
||||
if (data.velocities.length > 1) {
|
||||
const lastMoveEvent = data.velocities.pop();
|
||||
const velocityEvent = data.velocities.pop();
|
||||
if (data$$1.velocities.length > 1) {
|
||||
const lastMoveEvent = data$$1.velocities.pop();
|
||||
const velocityEvent = data$$1.velocities.pop();
|
||||
|
||||
const distance = lastMoveEvent.position - velocityEvent.position;
|
||||
const time = lastMoveEvent.time - velocityEvent.time;
|
||||
|
@ -2876,7 +2859,7 @@ function onTouchEnd (event) {
|
|||
}
|
||||
swiper.velocity *= params.freeModeMomentumVelocityRatio;
|
||||
|
||||
data.velocities.length = 0;
|
||||
data$$1.velocities.length = 0;
|
||||
let momentumDuration = 1000 * params.freeModeMomentumRatio;
|
||||
const momentumDistance = swiper.velocity * momentumDuration;
|
||||
|
||||
|
@ -2894,7 +2877,7 @@ function onTouchEnd (event) {
|
|||
}
|
||||
afterBouncePosition = swiper.maxTranslate();
|
||||
doBounce = true;
|
||||
data.allowMomentumBounce = true;
|
||||
data$$1.allowMomentumBounce = true;
|
||||
} else {
|
||||
newPosition = swiper.maxTranslate();
|
||||
}
|
||||
|
@ -2906,7 +2889,7 @@ function onTouchEnd (event) {
|
|||
}
|
||||
afterBouncePosition = swiper.minTranslate();
|
||||
doBounce = true;
|
||||
data.allowMomentumBounce = true;
|
||||
data$$1.allowMomentumBounce = true;
|
||||
} else {
|
||||
newPosition = swiper.minTranslate();
|
||||
}
|
||||
|
@ -2951,7 +2934,7 @@ function onTouchEnd (event) {
|
|||
swiper.transitionStart(true, swiper.swipeDirection);
|
||||
swiper.animating = true;
|
||||
$wrapperEl.transitionEnd(() => {
|
||||
if (!swiper || swiper.destroyed || !data.allowMomentumBounce) return;
|
||||
if (!swiper || swiper.destroyed || !data$$1.allowMomentumBounce) return;
|
||||
swiper.emit('momentumBounce');
|
||||
|
||||
swiper.setTransition(params.speed);
|
||||
|
@ -3216,12 +3199,7 @@ function setBreakpoint () {
|
|||
}
|
||||
|
||||
const breakpointParams = breakpointOnlyParams || swiper.originalParams;
|
||||
const directionChanged = breakpointParams.direction && breakpointParams.direction !== params.direction;
|
||||
const needsReLoop = params.loop && (breakpointParams.slidesPerView !== params.slidesPerView || directionChanged);
|
||||
|
||||
if (directionChanged && initialized) {
|
||||
swiper.changeDirection();
|
||||
}
|
||||
const needsReLoop = params.loop && (breakpointParams.slidesPerView !== params.slidesPerView);
|
||||
|
||||
Utils.extend(swiper.params, breakpointParams);
|
||||
|
||||
|
@ -3239,7 +3217,6 @@ function setBreakpoint () {
|
|||
swiper.updateSlides();
|
||||
swiper.slideTo((activeIndex - loopedSlides) + swiper.loopedSlides, 0, false);
|
||||
}
|
||||
|
||||
swiper.emit('breakpoint', breakpointParams);
|
||||
}
|
||||
}
|
||||
|
@ -3269,6 +3246,19 @@ function getBreakpoint (breakpoints) {
|
|||
|
||||
var breakpoints = { setBreakpoint, getBreakpoint };
|
||||
|
||||
const Browser = (function Browser() {
|
||||
function isSafari() {
|
||||
const ua = win.navigator.userAgent.toLowerCase();
|
||||
return (ua.indexOf('safari') >= 0 && ua.indexOf('chrome') < 0 && ua.indexOf('android') < 0);
|
||||
}
|
||||
return {
|
||||
isIE: !!win.navigator.userAgent.match(/Trident/g) || !!win.navigator.userAgent.match(/MSIE/g),
|
||||
isEdge: !!win.navigator.userAgent.match(/Edge/g),
|
||||
isSafari: isSafari(),
|
||||
isUiWebView: /(iPhone|iPod|iPad).*AppleWebKit(?!.*Safari)/i.test(win.navigator.userAgent),
|
||||
};
|
||||
}());
|
||||
|
||||
function addClasses () {
|
||||
const swiper = this;
|
||||
const {
|
||||
|
@ -3276,7 +3266,6 @@ function addClasses () {
|
|||
} = swiper;
|
||||
const suffixes = [];
|
||||
|
||||
suffixes.push('initialized');
|
||||
suffixes.push(params.direction);
|
||||
|
||||
if (params.freeMode) {
|
||||
|
@ -3528,8 +3517,6 @@ var defaults = {
|
|||
runCallbacksOnInit: true,
|
||||
};
|
||||
|
||||
/* eslint no-param-reassign: "off" */
|
||||
|
||||
const prototypes = {
|
||||
update,
|
||||
translate,
|
||||
|
@ -3618,7 +3605,7 @@ class Swiper extends SwiperClass {
|
|||
|
||||
if ($el.length > 1) {
|
||||
const swipers = [];
|
||||
$el.each((index, containerEl) => {
|
||||
$el.each((index$$1, containerEl) => {
|
||||
const newParams = Utils.extend({}, params, { el: containerEl });
|
||||
swipers.push(new Swiper(newParams));
|
||||
});
|
||||
|
@ -3827,52 +3814,6 @@ class Swiper extends SwiperClass {
|
|||
swiper.emit('update');
|
||||
}
|
||||
|
||||
changeDirection(newDirection, needUpdate = true) {
|
||||
const swiper = this;
|
||||
const currentDirection = swiper.params.direction;
|
||||
if (!newDirection) {
|
||||
// eslint-disable-next-line
|
||||
newDirection = currentDirection === 'horizontal' ? 'vertical' : 'horizontal';
|
||||
}
|
||||
if ((newDirection === currentDirection) || (newDirection !== 'horizontal' && newDirection !== 'vertical')) {
|
||||
return swiper;
|
||||
}
|
||||
|
||||
if (currentDirection === 'vertical') {
|
||||
swiper.$el
|
||||
.removeClass(`${swiper.params.containerModifierClass}vertical wp8-vertical`)
|
||||
.addClass(`${swiper.params.containerModifierClass}${newDirection}`);
|
||||
|
||||
if ((Browser.isIE || Browser.isEdge) && (Support.pointerEvents || Support.prefixedPointerEvents)) {
|
||||
swiper.$el.addClass(`${swiper.params.containerModifierClass}wp8-${newDirection}`);
|
||||
}
|
||||
}
|
||||
if (currentDirection === 'horizontal') {
|
||||
swiper.$el
|
||||
.removeClass(`${swiper.params.containerModifierClass}horizontal wp8-horizontal`)
|
||||
.addClass(`${swiper.params.containerModifierClass}${newDirection}`);
|
||||
|
||||
if ((Browser.isIE || Browser.isEdge) && (Support.pointerEvents || Support.prefixedPointerEvents)) {
|
||||
swiper.$el.addClass(`${swiper.params.containerModifierClass}wp8-${newDirection}`);
|
||||
}
|
||||
}
|
||||
|
||||
swiper.params.direction = newDirection;
|
||||
|
||||
swiper.slides.each((slideIndex, slideEl) => {
|
||||
if (newDirection === 'vertical') {
|
||||
slideEl.style.width = '';
|
||||
} else {
|
||||
slideEl.style.height = '';
|
||||
}
|
||||
});
|
||||
|
||||
swiper.emit('changeDirection');
|
||||
if (needUpdate) swiper.update();
|
||||
|
||||
return swiper;
|
||||
}
|
||||
|
||||
init() {
|
||||
const swiper = this;
|
||||
if (swiper.initialized) return;
|
||||
|
@ -4275,16 +4216,16 @@ const Mousewheel = {
|
|||
let delta = 0;
|
||||
const rtlFactor = swiper.rtlTranslate ? -1 : 1;
|
||||
|
||||
const data = Mousewheel.normalize(e);
|
||||
const data$$1 = Mousewheel.normalize(e);
|
||||
|
||||
if (params.forceToAxis) {
|
||||
if (swiper.isHorizontal()) {
|
||||
if (Math.abs(data.pixelX) > Math.abs(data.pixelY)) delta = data.pixelX * rtlFactor;
|
||||
if (Math.abs(data$$1.pixelX) > Math.abs(data$$1.pixelY)) delta = data$$1.pixelX * rtlFactor;
|
||||
else return true;
|
||||
} else if (Math.abs(data.pixelY) > Math.abs(data.pixelX)) delta = data.pixelY;
|
||||
} else if (Math.abs(data$$1.pixelY) > Math.abs(data$$1.pixelX)) delta = data$$1.pixelY;
|
||||
else return true;
|
||||
} else {
|
||||
delta = Math.abs(data.pixelX) > Math.abs(data.pixelY) ? -data.pixelX * rtlFactor : -data.pixelY;
|
||||
delta = Math.abs(data$$1.pixelX) > Math.abs(data$$1.pixelY) ? -data$$1.pixelX * rtlFactor : -data$$1.pixelY;
|
||||
}
|
||||
|
||||
if (delta === 0) return true;
|
||||
|
@ -4420,7 +4361,7 @@ const Pagination = {
|
|||
}
|
||||
bullets.removeClass(`${params.bulletActiveClass} ${params.bulletActiveClass}-next ${params.bulletActiveClass}-next-next ${params.bulletActiveClass}-prev ${params.bulletActiveClass}-prev-prev ${params.bulletActiveClass}-main`);
|
||||
if ($el.length > 1) {
|
||||
bullets.each((index, bullet) => {
|
||||
bullets.each((index$$1, bullet) => {
|
||||
const $bullet = $(bullet);
|
||||
const bulletIndex = $bullet.index();
|
||||
if (bulletIndex === current) {
|
||||
|
@ -4583,9 +4524,9 @@ const Pagination = {
|
|||
if (params.clickable) {
|
||||
$el.on('click', `.${params.bulletClass}`, function onClick(e) {
|
||||
e.preventDefault();
|
||||
let index = $(this).index() * swiper.params.slidesPerGroup;
|
||||
if (swiper.params.loop) index += swiper.loopedSlides;
|
||||
swiper.slideTo(index);
|
||||
let index$$1 = $(this).index() * swiper.params.slidesPerGroup;
|
||||
if (swiper.params.loop) index$$1 += swiper.loopedSlides;
|
||||
swiper.slideTo(index$$1);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -4698,12 +4639,6 @@ var pagination = {
|
|||
&& swiper.pagination.$el.length > 0
|
||||
&& !$(e.target).hasClass(swiper.params.pagination.bulletClass)
|
||||
) {
|
||||
const isHidden = swiper.pagination.$el.hasClass(swiper.params.pagination.hiddenClass);
|
||||
if (isHidden === true) {
|
||||
swiper.emit('paginationShow', swiper);
|
||||
} else {
|
||||
swiper.emit('paginationHide', swiper);
|
||||
}
|
||||
swiper.pagination.$el.toggleClass(swiper.params.pagination.hiddenClass);
|
||||
}
|
||||
},
|
||||
|
@ -4796,7 +4731,7 @@ const Scrollbar = {
|
|||
} else {
|
||||
$el[0].style.display = '';
|
||||
}
|
||||
if (swiper.params.scrollbar.hide) {
|
||||
if (swiper.params.scrollbarHide) {
|
||||
$el[0].style.opacity = 0;
|
||||
}
|
||||
Utils.extend(scrollbar, {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@ionic/react-router",
|
||||
"version": "4.8.0-rc.1",
|
||||
"version": "4.9.0-rc.2",
|
||||
"description": "React Router wrapper for @ionic/react",
|
||||
"keywords": [
|
||||
"ionic",
|
||||
|
@ -37,18 +37,20 @@
|
|||
"tslib": "*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ionic/react": "4.8.0-rc.1",
|
||||
"@ionic/core": "^4.9.0",
|
||||
"@ionic/react": "4.9.0-rc.2",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6",
|
||||
"react-router": "^5.0.1",
|
||||
"react-router-dom": "^5.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@ionic/react": "4.8.0-rc.0",
|
||||
"@ionic/core": "^4.9.0",
|
||||
"@ionic/react": "4.9.0-rc.2",
|
||||
"@types/jest": "^23.3.9",
|
||||
"@types/node": "12.6.9",
|
||||
"@types/react": "^16.8.19",
|
||||
"@types/react-dom": "^16.8.4",
|
||||
"@types/react": "^16.9.2",
|
||||
"@types/react-dom": "^16.9.0",
|
||||
"@types/react-router": "^5.0.3",
|
||||
"@types/react-router-dom": "^4.3.1",
|
||||
"jest": "^24.8.0",
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
import { match, RouteProps } from 'react-router-dom';
|
||||
|
||||
export interface IonRouteData {
|
||||
match: match<{ tab: string }> | null;
|
||||
childProps: RouteProps;
|
||||
}
|
|
@ -3,15 +3,16 @@ import { NavContext, NavContextState } from '@ionic/react';
|
|||
import { Location as HistoryLocation, UnregisterCallback } from 'history';
|
||||
import React from 'react';
|
||||
import { RouteComponentProps } from 'react-router-dom';
|
||||
import { ViewManager } from './ViewManager';
|
||||
import { StackManager } from './StackManager';
|
||||
import { generateUniqueId } from '../utils';
|
||||
import { LocationHistory } from '../utils/LocationHistory'
|
||||
import { ViewItem } from './ViewItem';
|
||||
import { ViewStack } from './RouteManagerContext';
|
||||
import { ViewStack } from './ViewStacks';
|
||||
|
||||
interface NavManagerProps extends RouteComponentProps {
|
||||
findViewInfoByLocation: (location: HistoryLocation) => {view?: ViewItem, viewStack?: ViewStack };
|
||||
findViewInfoById: (id: string) => {view?: ViewItem, viewStack?: ViewStack };
|
||||
getActiveIonPage: () => {view?: ViewItem, viewStack?: ViewStack };
|
||||
};
|
||||
interface NavManagerState extends NavContextState {};
|
||||
|
||||
|
@ -28,8 +29,10 @@ export class NavManager extends React.Component<NavManagerProps, NavManagerState
|
|||
getHistory: this.getHistory.bind(this),
|
||||
getLocation: this.getLocation.bind(this),
|
||||
navigate: this.navigate.bind(this),
|
||||
getViewManager: this.getViewManager.bind(this),
|
||||
currentPath: this.props.location.pathname
|
||||
getStackManager: this.getStackManager.bind(this),
|
||||
getPageManager: this.getPageManager.bind(this),
|
||||
currentPath: this.props.location.pathname,
|
||||
registerIonPage: () => {} //overridden in View for each IonPage
|
||||
}
|
||||
|
||||
this.listenUnregisterCallback = this.props.history.listen((location: HistoryLocation) => {
|
||||
|
@ -55,9 +58,9 @@ export class NavManager extends React.Component<NavManagerProps, NavManagerState
|
|||
}
|
||||
|
||||
goBack(defaultHref?: string) {
|
||||
const { view: leavingView } = this.props.findViewInfoByLocation(this.props.location);
|
||||
if (leavingView) {
|
||||
const { view: enteringView } = this.props.findViewInfoById(leavingView.prevId!);
|
||||
const { view: activeIonPage } = this.props.getActiveIonPage();
|
||||
if (activeIonPage) {
|
||||
const { view: enteringView } = this.props.findViewInfoById(activeIonPage.prevId!);
|
||||
if (enteringView) {
|
||||
const lastLocation = this.locationHistory.findLastLocation(enteringView.routeData.match.url);
|
||||
if (lastLocation) {
|
||||
|
@ -81,12 +84,16 @@ export class NavManager extends React.Component<NavManagerProps, NavManagerState
|
|||
return this.props.location as any;
|
||||
}
|
||||
|
||||
navigate(path: string, direction?: RouterDirection) {
|
||||
navigate(path: string, direction?: RouterDirection | 'none') {
|
||||
this.props.history.push(path, { direction });
|
||||
}
|
||||
|
||||
getViewManager() {
|
||||
return ViewManager;
|
||||
getPageManager() {
|
||||
return (children: any) => children;
|
||||
}
|
||||
|
||||
getStackManager() {
|
||||
return StackManager;
|
||||
}
|
||||
|
||||
render() {
|
||||
|
|
|
@ -1,32 +1,22 @@
|
|||
import React, { ReactNode } from 'react';
|
||||
import { NavDirection } from '@ionic/core';
|
||||
import { ViewItem } from './ViewItem';
|
||||
|
||||
export interface ViewStack {
|
||||
routerOutlet: HTMLIonRouterOutletElement;
|
||||
activeId?: string,
|
||||
views: ViewItem[]
|
||||
}
|
||||
|
||||
export interface ViewStacks {
|
||||
[key: string]: ViewStack;
|
||||
}
|
||||
import { ViewStacks } from './ViewStacks';
|
||||
|
||||
export interface RouteManagerContextState {
|
||||
syncView: (page: HTMLElement, viewId: string) => void;
|
||||
hideView: (viewId: string) => void;
|
||||
viewStacks: ViewStacks;
|
||||
setupIonRouter: (id: string, children: ReactNode, routerOutlet: HTMLIonRouterOutletElement) => void;
|
||||
setupIonRouter: (id: string, children: ReactNode, routerOutlet: HTMLIonRouterOutletElement) => Promise<void>;
|
||||
removeViewStack: (stack: string) => void;
|
||||
renderChild: (item: ViewItem) => void;
|
||||
transitionView: (enteringEl: HTMLElement, leavingEl: HTMLElement, ionRouterOuter: HTMLIonRouterOutletElement, direction: NavDirection) => void;
|
||||
}
|
||||
|
||||
export const RouteManagerContext = /*@__PURE__*/React.createContext<RouteManagerContextState>({
|
||||
viewStacks: {},
|
||||
viewStacks: new ViewStacks(),
|
||||
syncView: () => { navContextNotFoundError(); },
|
||||
hideView: () => { navContextNotFoundError(); },
|
||||
setupIonRouter: () => { navContextNotFoundError() },
|
||||
setupIonRouter: () => { return Promise.reject(navContextNotFoundError()) },
|
||||
removeViewStack: () => { navContextNotFoundError(); },
|
||||
renderChild: () => { navContextNotFoundError(); },
|
||||
transitionView: () => { navContextNotFoundError(); }
|
||||
});
|
||||
|
||||
|
|
|
@ -1,44 +1,54 @@
|
|||
import { NavDirection } from '@ionic/core';
|
||||
import { RouterDirection } from '@ionic/react';
|
||||
import { Action as HistoryAction, Location as HistoryLocation, UnregisterCallback } from 'history';
|
||||
import React from 'react';
|
||||
import { BrowserRouter, BrowserRouterProps, match, matchPath, Redirect, Route, RouteComponentProps, RouteProps, withRouter } from 'react-router-dom';
|
||||
import { BrowserRouter, BrowserRouterProps, matchPath, RouteComponentProps, withRouter } from 'react-router-dom';
|
||||
import { generateUniqueId } from '../utils';
|
||||
import { IonRouteData } from './IonRouteData';
|
||||
import { NavManager } from './NavManager';
|
||||
import { RouteManagerContext, RouteManagerContextState, ViewStack, ViewStacks } from './RouteManagerContext';
|
||||
import { RouteManagerContext, RouteManagerContextState } from './RouteManagerContext';
|
||||
import { ViewItem } from './ViewItem';
|
||||
import { ViewStacks, ViewStack } from './ViewStacks';
|
||||
|
||||
interface RouterManagerProps extends RouteComponentProps { }
|
||||
|
||||
interface RouteManagerState extends RouteManagerContextState { }
|
||||
interface RouteManagerProps extends RouteComponentProps { }
|
||||
|
||||
interface IonRouteData {
|
||||
match: match<{ tab: string }> | null;
|
||||
childProps: RouteProps;
|
||||
interface RouteManagerState extends RouteManagerContextState {
|
||||
location?: HistoryLocation,
|
||||
action?: HistoryAction
|
||||
}
|
||||
|
||||
class RouteManager extends React.Component<RouterManagerProps, RouteManagerState> {
|
||||
class RouteManager extends React.Component<RouteManagerProps, RouteManagerState> {
|
||||
listenUnregisterCallback: UnregisterCallback | undefined;
|
||||
activeViewId?: string;
|
||||
prevViewId?: string;
|
||||
activeIonPageId?: string;
|
||||
|
||||
constructor(props: RouterManagerProps) {
|
||||
constructor(props: RouteManagerProps) {
|
||||
super(props);
|
||||
this.listenUnregisterCallback = this.props.history.listen(this.historyChange.bind(this));
|
||||
this.state = {
|
||||
viewStacks: {},
|
||||
viewStacks: new ViewStacks(),
|
||||
hideView: this.hideView.bind(this),
|
||||
setupIonRouter: this.setupIonRouter.bind(this),
|
||||
removeViewStack: this.removeViewStack.bind(this),
|
||||
renderChild: this.renderChild.bind(this),
|
||||
transitionView: this.transitionView.bind(this)
|
||||
syncView: this.syncView.bind(this),
|
||||
transitionView: this.transitionView.bind(this),
|
||||
};
|
||||
}
|
||||
|
||||
componentDidUpdate(_prevProps: RouteManagerProps, prevState: RouteManagerState) {
|
||||
// Trigger a page change if the location or action is different
|
||||
if (this.state.location && prevState.location !== this.state.location || prevState.action !== this.state.action) {
|
||||
this.setActiveView(this.state.location!, this.state.action!);
|
||||
}
|
||||
}
|
||||
|
||||
hideView(viewId: string) {
|
||||
const viewStacks = Object.assign({}, this.state.viewStacks);
|
||||
const { view } = this.findViewInfoById(viewId, viewStacks);
|
||||
const viewStacks = Object.assign(new ViewStacks(), this.state.viewStacks);
|
||||
const { view } = viewStacks.findViewInfoById(viewId);
|
||||
if (view) {
|
||||
view.show = false;
|
||||
view.ionPageElement = undefined;
|
||||
view.isIonRoute = false;
|
||||
view.key = generateUniqueId();
|
||||
this.setState({
|
||||
viewStacks
|
||||
|
@ -47,146 +57,111 @@ class RouteManager extends React.Component<RouterManagerProps, RouteManagerState
|
|||
}
|
||||
|
||||
historyChange(location: HistoryLocation, action: HistoryAction) {
|
||||
this.setActiveView(location, action);
|
||||
}
|
||||
|
||||
findViewInfoByLocation(location: HistoryLocation, viewStacks: ViewStacks) {
|
||||
let view: ViewItem<IonRouteData> | undefined;
|
||||
let match: IonRouteData["match"] | null | undefined;
|
||||
let viewStack: ViewStack | undefined;
|
||||
const keys = Object.keys(viewStacks);
|
||||
keys.some(key => {
|
||||
const vs = viewStacks[key];
|
||||
return vs.views.some(x => {
|
||||
const matchProps = {
|
||||
exact: x.routeData.childProps.exact,
|
||||
path: x.routeData.childProps.path || x.routeData.childProps.from,
|
||||
component: x.routeData.childProps.component
|
||||
};
|
||||
match = matchPath(location.pathname, matchProps)
|
||||
if (match) {
|
||||
view = x;
|
||||
viewStack = vs;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
this.setState({
|
||||
location,
|
||||
action
|
||||
})
|
||||
|
||||
const result = { view, viewStack, match };
|
||||
return result;
|
||||
}
|
||||
|
||||
findViewInfoById(id: string, viewStacks: ViewStacks) {
|
||||
let view: ViewItem<IonRouteData> | undefined;
|
||||
let viewStack: ViewStack | undefined;
|
||||
const keys = Object.keys(viewStacks);
|
||||
keys.some(key => {
|
||||
const vs = viewStacks[key];
|
||||
view = vs.views.find(x => x.id === id);
|
||||
if (view) {
|
||||
viewStack = vs;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
return { view, viewStack };
|
||||
}
|
||||
|
||||
setActiveView(location: HistoryLocation, action: HistoryAction) {
|
||||
const viewStacks = Object.assign({}, this.state.viewStacks);
|
||||
const { view: enteringView, viewStack: enteringViewStack, match } = this.findViewInfoByLocation(location, viewStacks);
|
||||
let direction: NavDirection = location.state && location.state.direction;
|
||||
const viewStacks = Object.assign(new ViewStacks(), this.state.viewStacks);
|
||||
let direction: RouterDirection = location.state && location.state.direction || 'forward';
|
||||
let leavingView: ViewItem | undefined;
|
||||
const viewStackKeys = viewStacks.getKeys();
|
||||
|
||||
if (!enteringViewStack) {
|
||||
return;
|
||||
}
|
||||
viewStackKeys.forEach(key => {
|
||||
const { view: enteringView, viewStack: enteringViewStack, match } = viewStacks.findViewInfoByLocation(location, key);
|
||||
if (!enteringView || !enteringViewStack) {
|
||||
return;
|
||||
}
|
||||
leavingView = viewStacks.findViewInfoById(this.activeIonPageId).view;
|
||||
|
||||
const { view: leavingView } = this.findViewInfoById(this.activeViewId!, viewStacks);
|
||||
|
||||
if (leavingView && leavingView.routeData.match!.url === location.pathname) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (enteringView) {
|
||||
/**
|
||||
* If the page is being pushed into the stack by another view,
|
||||
* record the view that originally directed to the new view for back button purposes.
|
||||
*/
|
||||
if (!enteringView.show && action === 'PUSH') {
|
||||
enteringView.prevId = leavingView && leavingView.id;
|
||||
if (leavingView && leavingView.routeData.match!.url === location.pathname) {
|
||||
return;
|
||||
}
|
||||
|
||||
enteringView.show = true;
|
||||
enteringView.mount = true;
|
||||
enteringView.routeData.match = match!;
|
||||
enteringViewStack.activeId = enteringView.id;
|
||||
this.activeViewId = enteringView.id;
|
||||
if (enteringView) {
|
||||
|
||||
if (leavingView) {
|
||||
this.prevViewId = leavingView.id
|
||||
if (leavingView.routeData.match!.params.tab === enteringView.routeData.match.params.tab) {
|
||||
if (action === 'PUSH') {
|
||||
direction = direction || 'forward';
|
||||
} else {
|
||||
direction = direction || 'back';
|
||||
leavingView.mount = false;
|
||||
if (enteringView.isIonRoute) {
|
||||
enteringView.show = true;
|
||||
enteringView.mount = true;
|
||||
enteringView.routeData.match = match!;
|
||||
|
||||
this.activeIonPageId = enteringView.id;
|
||||
|
||||
if (leavingView) {
|
||||
if (direction === 'forward') {
|
||||
if (action === 'PUSH') {
|
||||
/**
|
||||
* If the page is being pushed into the stack by another view,
|
||||
* record the view that originally directed to the new view for back button purposes.
|
||||
*/
|
||||
enteringView.prevId = leavingView.id;
|
||||
} else {
|
||||
direction = direction || 'back';
|
||||
leavingView.mount = false;
|
||||
}
|
||||
} else if (action === 'REPLACE') {
|
||||
leavingView.mount = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* If the leaving view is a Redirect, take it out of the rendering phase.
|
||||
*/
|
||||
if(leavingView.element.type === Redirect) {
|
||||
leavingView.mount = false;
|
||||
leavingView.show = false;
|
||||
}
|
||||
|
||||
|
||||
if (leavingView.element.type === Route && leavingView.element.props.render) {
|
||||
if (leavingView.element.props.render().type === Redirect) {
|
||||
leavingView.mount = false;
|
||||
leavingView.show = false;
|
||||
}
|
||||
} else if (leavingView.element.type === Redirect) {
|
||||
leavingView.mount = false;
|
||||
leavingView.show = false;
|
||||
} else {
|
||||
enteringView.show = true;
|
||||
enteringView.mount = true;
|
||||
enteringView.routeData.match = match!;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.setState({
|
||||
viewStacks
|
||||
}, () => {
|
||||
const enteringEl = enteringView.ref && enteringView.ref.current ? enteringView.ref.current : undefined;
|
||||
const leavingEl = leavingView && leavingView.ref && leavingView.ref.current ? leavingView.ref.current : undefined;
|
||||
this.transitionView(
|
||||
enteringEl!,
|
||||
leavingEl!,
|
||||
enteringViewStack.routerOutlet,
|
||||
leavingEl && leavingEl.innerHTML !== '' ? direction : undefined!) // Don't animate from an empty view
|
||||
});
|
||||
if (leavingView) {
|
||||
if (!leavingView.isIonRoute) {
|
||||
leavingView.mount = false;
|
||||
leavingView.show = false;
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({
|
||||
viewStacks
|
||||
}, () => {
|
||||
const { view: enteringView, viewStack } = this.state.viewStacks.findViewInfoById(this.activeIonPageId)
|
||||
if (enteringView && viewStack) {
|
||||
const enteringEl = enteringView.ionPageElement ? enteringView.ionPageElement : undefined;
|
||||
const leavingEl = leavingView && leavingView.ionPageElement ? leavingView.ionPageElement : undefined;
|
||||
|
||||
if (enteringEl) {
|
||||
// Don't animate from an empty view
|
||||
const navDirection = leavingEl && leavingEl.innerHTML === '' ? undefined : direction === 'none' ? undefined : direction;
|
||||
this.transitionView(
|
||||
enteringEl!,
|
||||
leavingEl!,
|
||||
viewStack.routerOutlet,
|
||||
navDirection)
|
||||
} else if (leavingEl) {
|
||||
leavingEl.classList.add('ion-page-hidden');
|
||||
leavingEl.setAttribute('aria-hidden', 'true');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.listenUnregisterCallback && this.listenUnregisterCallback();
|
||||
}
|
||||
|
||||
setupIonRouter(id: string, children: any, routerOutlet: HTMLIonRouterOutletElement) {
|
||||
async setupIonRouter(id: string, children: any, routerOutlet: HTMLIonRouterOutletElement) {
|
||||
const views: ViewItem[] = [];
|
||||
let activeId: string | undefined;
|
||||
const ionRouterOutlet = React.Children.only(children) as React.ReactElement;
|
||||
|
||||
React.Children.forEach(ionRouterOutlet.props.children, (child: React.ReactElement) => {
|
||||
views.push(createViewItem(child, this.props.history.location));
|
||||
});
|
||||
|
||||
this.registerViewStack(id, activeId, views, routerOutlet, this.props.location);
|
||||
await this.registerViewStack(id, activeId, views, routerOutlet, this.props.location);
|
||||
|
||||
function createViewItem(child: React.ReactElement<any>, location: HistoryLocation) {
|
||||
const viewId = generateUniqueId();
|
||||
const key = generateUniqueId();
|
||||
const element = child;
|
||||
const route = child;
|
||||
const matchProps = {
|
||||
exact: child.props.exact,
|
||||
path: child.props.path || child.props.from,
|
||||
|
@ -200,89 +175,87 @@ class RouteManager extends React.Component<RouterManagerProps, RouteManagerState
|
|||
match,
|
||||
childProps: child.props
|
||||
},
|
||||
element,
|
||||
route: route,
|
||||
mount: true,
|
||||
show: !!match,
|
||||
ref: React.createRef()
|
||||
isIonRoute: false
|
||||
};
|
||||
if (!!match) {
|
||||
if (!!match && view.isIonRoute) {
|
||||
activeId = viewId;
|
||||
};
|
||||
return view;
|
||||
}
|
||||
}
|
||||
|
||||
registerViewStack(stack: string, activeId: string | undefined, stackItems: ViewItem[], routerOutlet: HTMLIonRouterOutletElement, location: HistoryLocation) {
|
||||
this.setState((prevState) => {
|
||||
const prevViewStacks = Object.assign({}, prevState.viewStacks);
|
||||
prevViewStacks[stack] = {
|
||||
activeId: activeId,
|
||||
views: stackItems,
|
||||
routerOutlet
|
||||
};
|
||||
return {
|
||||
viewStacks: prevViewStacks
|
||||
};
|
||||
}, () => {
|
||||
const { view: activeView } = this.findViewInfoById(activeId!, this.state.viewStacks);
|
||||
async registerViewStack(stack: string, activeId: string | undefined, stackItems: ViewItem[], routerOutlet: HTMLIonRouterOutletElement, _location: HistoryLocation) {
|
||||
|
||||
if (activeView) {
|
||||
this.prevViewId = this.activeViewId;
|
||||
this.activeViewId = activeView.id;
|
||||
const direction = location.state && location.state.direction;
|
||||
const { view: prevView } = this.findViewInfoById(this.prevViewId!, this.state.viewStacks);
|
||||
this.transitionView(
|
||||
activeView.ref!.current!,
|
||||
prevView && prevView.ref!.current || undefined!,
|
||||
routerOutlet,
|
||||
direction);
|
||||
}
|
||||
return new Promise((resolve) => {
|
||||
this.setState((prevState) => {
|
||||
const prevViewStacks = Object.assign(new ViewStacks, prevState.viewStacks);
|
||||
const newStack: ViewStack = {
|
||||
id: stack,
|
||||
views: stackItems,
|
||||
routerOutlet
|
||||
};
|
||||
if (activeId) {
|
||||
this.activeIonPageId = activeId;
|
||||
}
|
||||
prevViewStacks.set(stack, newStack);
|
||||
return {
|
||||
viewStacks: prevViewStacks
|
||||
};
|
||||
}, () => {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
removeViewStack(stack: string) {
|
||||
const viewStacks = Object.assign({}, this.state.viewStacks);
|
||||
delete viewStacks[stack];
|
||||
const viewStacks = Object.assign(new ViewStacks(), this.state.viewStacks);
|
||||
viewStacks.delete(stack);
|
||||
this.setState({
|
||||
viewStacks
|
||||
});
|
||||
}
|
||||
|
||||
renderChild(item: ViewItem<IonRouteData>) {
|
||||
const component = React.cloneElement(item.element, {
|
||||
computedMatch: item.routeData.match
|
||||
});
|
||||
return component;
|
||||
}
|
||||
syncView(page: HTMLElement, viewId: string) {
|
||||
this.setState((state) => {
|
||||
|
||||
findActiveView(views: ViewItem[]) {
|
||||
let view: ViewItem<IonRouteData> | undefined;
|
||||
views.some(x => {
|
||||
const match = matchPath(this.props.location.pathname, x.routeData.childProps)
|
||||
if (match) {
|
||||
view = x;
|
||||
return true;
|
||||
const viewStacks = Object.assign(new ViewStacks(), state.viewStacks);
|
||||
const { view } = viewStacks.findViewInfoById(viewId);
|
||||
|
||||
view!.ionPageElement = page;
|
||||
view!.isIonRoute = true;
|
||||
|
||||
return {
|
||||
viewStacks
|
||||
}
|
||||
return false;
|
||||
});
|
||||
return view;
|
||||
|
||||
}, () => {
|
||||
this.setActiveView(this.state.location || this.props.location, this.state.action!);
|
||||
})
|
||||
}
|
||||
|
||||
transitionView(enteringEl: HTMLElement, leavingEl: HTMLElement, ionRouterOuter: HTMLIonRouterOutletElement, direction: NavDirection) {
|
||||
transitionView(enteringEl: HTMLElement, leavingEl: HTMLElement, ionRouterOutlet: HTMLIonRouterOutletElement, direction?: NavDirection) {
|
||||
/**
|
||||
* Super hacky workaround to make sure ionRouterOutlet is available
|
||||
* since transitionView might be called before IonRouterOutlet is fully mounted
|
||||
*/
|
||||
if (ionRouterOuter && ionRouterOuter.componentOnReady) {
|
||||
this.commitView(enteringEl, leavingEl, ionRouterOuter, direction);
|
||||
if (ionRouterOutlet && ionRouterOutlet.componentOnReady) {
|
||||
this.commitView(enteringEl, leavingEl, ionRouterOutlet, direction);
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
this.transitionView(enteringEl, leavingEl, ionRouterOuter, direction);
|
||||
this.transitionView(enteringEl, leavingEl, ionRouterOutlet, direction);
|
||||
}, 10);
|
||||
}
|
||||
}
|
||||
|
||||
private async commitView(enteringEl: HTMLElement, leavingEl: HTMLElement, ionRouterOuter: HTMLIonRouterOutletElement, direction: NavDirection) {
|
||||
private async commitView(enteringEl: HTMLElement, leavingEl: HTMLElement, ionRouterOuter: HTMLIonRouterOutletElement, direction?: NavDirection) {
|
||||
|
||||
if (enteringEl === leavingEl) {
|
||||
return;
|
||||
}
|
||||
|
||||
await ionRouterOuter.commit(enteringEl, leavingEl, {
|
||||
deepWait: true,
|
||||
duration: direction === undefined ? 0 : undefined,
|
||||
|
@ -292,9 +265,7 @@ class RouteManager extends React.Component<RouterManagerProps, RouteManagerState
|
|||
});
|
||||
|
||||
if (leavingEl && (enteringEl !== leavingEl)) {
|
||||
/**
|
||||
* add hidden attributes
|
||||
*/
|
||||
/** add hidden attributes */
|
||||
leavingEl.classList.add('ion-page-hidden');
|
||||
leavingEl.setAttribute('aria-hidden', 'true');
|
||||
}
|
||||
|
@ -304,8 +275,9 @@ class RouteManager extends React.Component<RouterManagerProps, RouteManagerState
|
|||
return (
|
||||
<RouteManagerContext.Provider value={this.state}>
|
||||
<NavManager {...this.props}
|
||||
findViewInfoById={(id: string) => this.findViewInfoById(id, this.state.viewStacks)}
|
||||
findViewInfoByLocation={(location: HistoryLocation) => this.findViewInfoByLocation(location, this.state.viewStacks)}
|
||||
findViewInfoById={(id: string) => this.state.viewStacks.findViewInfoById(id)}
|
||||
findViewInfoByLocation={(location: HistoryLocation) => this.state.viewStacks.findViewInfoByLocation(location)}
|
||||
getActiveIonPage={() => this.state.viewStacks.findViewInfoById(this.activeIonPageId)}
|
||||
>
|
||||
{this.props.children}
|
||||
</NavManager>
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
import React from 'react';
|
||||
import { generateUniqueId, isDevMode } from '../utils';
|
||||
import { View } from './View';
|
||||
import { ViewTransitionManager } from './ViewTransitionManager';
|
||||
import { RouteManagerContext } from './RouteManagerContext';
|
||||
import { ViewItem } from './ViewItem';
|
||||
|
||||
type StackManagerProps = {
|
||||
id?: string;
|
||||
};
|
||||
|
||||
type StackManagerState = {}
|
||||
|
||||
export class StackManager extends React.Component<StackManagerProps, StackManagerState> {
|
||||
routerOutletEl: React.RefObject<HTMLIonRouterOutletElement> = React.createRef();
|
||||
context!: React.ContextType<typeof RouteManagerContext>;
|
||||
id: string;
|
||||
|
||||
constructor(props: StackManagerProps) {
|
||||
super(props);
|
||||
this.id = this.props.id || generateUniqueId();
|
||||
this.handleViewSync = this.handleViewSync.bind(this);
|
||||
this.handleHideView = this.handleHideView.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.context.setupIonRouter(this.id, this.props.children, this.routerOutletEl.current!);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.context.removeViewStack(this.id);
|
||||
}
|
||||
|
||||
handleViewSync(page: HTMLElement, viewId: string) {
|
||||
this.context.syncView(page, viewId);
|
||||
}
|
||||
|
||||
handleHideView(viewId: string) {
|
||||
this.context.hideView(viewId);
|
||||
}
|
||||
|
||||
renderChild(item: ViewItem) {
|
||||
const component = React.cloneElement(item.route, {
|
||||
computedMatch: item.routeData.match
|
||||
});
|
||||
return component;
|
||||
}
|
||||
|
||||
render() {
|
||||
const context = this.context;
|
||||
const viewStack = context.viewStacks.get(this.id);
|
||||
const views = (viewStack || { views: [] }).views.filter(x => x.show);
|
||||
const ionRouterOutlet = React.Children.only(this.props.children) as React.ReactElement;
|
||||
|
||||
const childElements = views.map((view) => {
|
||||
return (
|
||||
<ViewTransitionManager
|
||||
id={view.id}
|
||||
key={view.key}
|
||||
mount={view.mount}
|
||||
>
|
||||
<View
|
||||
onViewSync={this.handleViewSync}
|
||||
onHideView={this.handleHideView}
|
||||
view={view}
|
||||
>
|
||||
{this.renderChild(view)}
|
||||
</View>
|
||||
</ViewTransitionManager>
|
||||
);
|
||||
});
|
||||
|
||||
const elementProps: any = {
|
||||
ref: this.routerOutletEl
|
||||
}
|
||||
|
||||
if(isDevMode()) {
|
||||
elementProps['data-stack-id'] = this.id
|
||||
}
|
||||
|
||||
const routerOutletChild = React.cloneElement(ionRouterOutlet, elementProps, childElements);
|
||||
|
||||
|
||||
return routerOutletChild;
|
||||
}
|
||||
|
||||
static get contextType() {
|
||||
return RouteManagerContext;
|
||||
}
|
||||
}
|
|
@ -1,48 +1,44 @@
|
|||
import React from 'react';
|
||||
import { IonLifeCycleContext } from '@ionic/react';
|
||||
import { IonLifeCycleContext, NavContext } from '@ionic/react';
|
||||
import { ViewItem } from './ViewItem';
|
||||
import { Route, Redirect } from 'react-router-dom';
|
||||
import { isDevMode } from '../utils';
|
||||
|
||||
type Props = React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>;
|
||||
|
||||
interface InternalProps extends React.HTMLAttributes<HTMLElement> {
|
||||
forwardedRef?: React.RefObject<HTMLElement>,
|
||||
interface ViewProps extends React.HTMLAttributes<HTMLElement> {
|
||||
onViewSync: (page: HTMLElement, viewId: string) => void;
|
||||
onHideView: (viewId: string) => void;
|
||||
view: ViewItem;
|
||||
};
|
||||
|
||||
type ExternalProps = Props & {
|
||||
ref?: React.RefObject<HTMLElement>
|
||||
};
|
||||
interface StackViewState { }
|
||||
|
||||
interface StackViewState {
|
||||
ref: any;
|
||||
}
|
||||
|
||||
class ViewInternal extends React.Component<InternalProps, StackViewState> {
|
||||
/**
|
||||
* The View component helps manage the IonPage's lifecycle and registration
|
||||
*/
|
||||
export class View extends React.Component<ViewProps, StackViewState> {
|
||||
context!: React.ContextType<typeof IonLifeCycleContext>;
|
||||
|
||||
constructor(props: InternalProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
ref: null
|
||||
}
|
||||
}
|
||||
ionPage?: HTMLElement;
|
||||
|
||||
componentDidMount() {
|
||||
const { forwardedRef } = this.props;
|
||||
this.setState({ ref: forwardedRef });
|
||||
if (forwardedRef && forwardedRef.current) {
|
||||
forwardedRef.current.addEventListener('ionViewWillEnter', this.ionViewWillEnterHandler.bind(this));
|
||||
forwardedRef.current.addEventListener('ionViewDidEnter', this.ionViewDidEnterHandler.bind(this));
|
||||
forwardedRef.current.addEventListener('ionViewWillLeave', this.ionViewWillLeaveHandler.bind(this));
|
||||
forwardedRef.current.addEventListener('ionViewDidLeave', this.ionViewDidLeaveHandler.bind(this));
|
||||
/**
|
||||
* If we can tell if view is a redirect, hide it so it will work again in future
|
||||
*/
|
||||
const { view } = this.props;
|
||||
if (view.route.type === Redirect) {
|
||||
this.props.onHideView(view.id);
|
||||
} else if (view.route.type === Route && view.route.props.render) {
|
||||
if (view.route.props.render().type === Redirect) {
|
||||
this.props.onHideView(view.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
const { forwardedRef } = this.props;
|
||||
if (forwardedRef && forwardedRef.current) {
|
||||
forwardedRef.current.removeEventListener('ionViewWillEnter', this.ionViewWillEnterHandler.bind(this));
|
||||
forwardedRef.current.removeEventListener('ionViewDidEnter', this.ionViewDidEnterHandler.bind(this));
|
||||
forwardedRef.current.removeEventListener('ionViewWillLeave', this.ionViewWillLeaveHandler.bind(this));
|
||||
forwardedRef.current.removeEventListener('ionViewDidLeave', this.ionViewDidLeaveHandler.bind(this));
|
||||
if (this.ionPage) {
|
||||
this.ionPage.removeEventListener('ionViewWillEnter', this.ionViewWillEnterHandler.bind(this));
|
||||
this.ionPage.removeEventListener('ionViewDidEnter', this.ionViewDidEnterHandler.bind(this));
|
||||
this.ionPage.removeEventListener('ionViewWillLeave', this.ionViewWillLeaveHandler.bind(this));
|
||||
this.ionPage.removeEventListener('ionViewDidLeave', this.ionViewDidLeaveHandler.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -62,28 +58,39 @@ class ViewInternal extends React.Component<InternalProps, StackViewState> {
|
|||
this.context.ionViewDidLeave();
|
||||
}
|
||||
|
||||
registerIonPage(page: HTMLElement) {
|
||||
this.ionPage = page;
|
||||
this.ionPage.addEventListener('ionViewWillEnter', this.ionViewWillEnterHandler.bind(this));
|
||||
this.ionPage.addEventListener('ionViewDidEnter', this.ionViewDidEnterHandler.bind(this));
|
||||
this.ionPage.addEventListener('ionViewWillLeave', this.ionViewWillLeaveHandler.bind(this));
|
||||
this.ionPage.addEventListener('ionViewDidLeave', this.ionViewDidLeaveHandler.bind(this));
|
||||
this.ionPage.classList.add('ion-page-invisible');
|
||||
if(isDevMode()) {
|
||||
this.ionPage.setAttribute('data-view-id', this.props.view.id);
|
||||
}
|
||||
this.props.onViewSync(page, this.props.view.id);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { className, children, forwardedRef, ...rest } = this.props;
|
||||
const { ref } = this.state;
|
||||
return (
|
||||
<div
|
||||
className={className ? `ion-page ${className}` : 'ion-page'}
|
||||
ref={forwardedRef as any}
|
||||
{...rest}
|
||||
>
|
||||
{ref && children}
|
||||
</div>
|
||||
)
|
||||
<NavContext.Consumer>
|
||||
{value => {
|
||||
const newProvider = {
|
||||
...value,
|
||||
registerIonPage: this.registerIonPage.bind(this)
|
||||
}
|
||||
return (
|
||||
<NavContext.Provider value={newProvider}>
|
||||
{this.props.children}
|
||||
</NavContext.Provider>
|
||||
);
|
||||
|
||||
}}
|
||||
</NavContext.Consumer>
|
||||
);
|
||||
}
|
||||
|
||||
static get contextType() {
|
||||
return IonLifeCycleContext;
|
||||
}
|
||||
}
|
||||
|
||||
function forwardRef(props: InternalProps, ref: React.RefObject<HTMLElement>) {
|
||||
return <ViewInternal forwardedRef={ref} {...props} />;
|
||||
}
|
||||
forwardRef.displayName = 'View';
|
||||
|
||||
export const View = /*@__PURE__*/React.forwardRef<HTMLElement, ExternalProps>(forwardRef as any);
|
||||
|
|
|
@ -1,10 +1,26 @@
|
|||
export interface ViewItem<RouteData = any> {
|
||||
/** The generated id of the view */
|
||||
id: string;
|
||||
/** The key used by React. A new key is generated each time the view comes into the DOM so React thinks its a completely new element. */
|
||||
key: string;
|
||||
element: React.ReactElement<any>;
|
||||
ref?: React.RefObject<HTMLElement>;
|
||||
/** The <Route /> or <Redirect /> component associated with the view */
|
||||
route: React.ReactElement<any>;
|
||||
/** The reference to the <IonPage /> element. */
|
||||
ionPageElement?: HTMLElement;
|
||||
/** The routeData for the view. */
|
||||
routeData: RouteData;
|
||||
/** Used to track which page pushed the page into view. Used for back button purposes. */
|
||||
prevId?: string;
|
||||
/**
|
||||
* Mount is used for page transitions. If mount is false, it keeps the view in the DOM long enough to finish the transition.
|
||||
*/
|
||||
mount: boolean;
|
||||
/**
|
||||
* Show determines if the view will be in the DOM or not
|
||||
*/
|
||||
show: boolean;
|
||||
/**
|
||||
* An IonRoute is a Route that contains an IonPage. Only IonPages participate in transition and lifecycle events.
|
||||
*/
|
||||
isIonRoute: boolean;
|
||||
}
|
||||
|
|
|
@ -1,75 +1,17 @@
|
|||
import React from 'react';
|
||||
import { generateUniqueId } from '../utils';
|
||||
import { View } from './View';
|
||||
import { ViewItemManager } from './ViewItemManager';
|
||||
import { RouteManagerContext } from './RouteManagerContext';
|
||||
import * as React from 'react';
|
||||
import { deprecationWarning } from '../utils';
|
||||
|
||||
declare global {
|
||||
namespace JSX {
|
||||
interface IntrinsicElements {
|
||||
'ion-router-outlet': any;
|
||||
}
|
||||
}
|
||||
}
|
||||
interface ViewManagerProps { }
|
||||
|
||||
type ViewManagerProps = {
|
||||
id?: string;
|
||||
};
|
||||
|
||||
type ViewManagerState = {}
|
||||
interface ViewManagerState { }
|
||||
|
||||
export class ViewManager extends React.Component<ViewManagerProps, ViewManagerState> {
|
||||
containerEl: React.RefObject<HTMLIonRouterOutletElement> = React.createRef();
|
||||
context!: React.ContextType<typeof RouteManagerContext>;
|
||||
id: string;
|
||||
|
||||
constructor(props: ViewManagerProps) {
|
||||
super(props);
|
||||
this.id = this.props.id || generateUniqueId();
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.context.setupIonRouter(this.id, this.props.children, this.containerEl.current!);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.context.removeViewStack(this.id);
|
||||
deprecationWarning('As of @ionic/react RC2, ViewManager is no longer needed and can be removed. This component is now deprecated will be removed from @ionic/react final.')
|
||||
}
|
||||
|
||||
render() {
|
||||
const context = this.context;
|
||||
const viewStack = context.viewStacks[this.id];
|
||||
const activeId = viewStack ? viewStack.activeId : '';
|
||||
const views = (viewStack || { views: [] }).views.filter(x => x.show);
|
||||
return (
|
||||
<ion-router-outlet data-id={this.id} ref={this.containerEl}>
|
||||
{views.map((item) => {
|
||||
let props: any = {};
|
||||
if (item.id === activeId) {
|
||||
props = {
|
||||
'className': ' ion-page-invisible'
|
||||
};
|
||||
}
|
||||
return (
|
||||
<ViewItemManager
|
||||
id={item.id}
|
||||
key={item.key}
|
||||
mount={item.mount}
|
||||
>
|
||||
<View
|
||||
ref={item.ref}
|
||||
{...props}
|
||||
>
|
||||
{this.context.renderChild(item)}
|
||||
</View>
|
||||
</ViewItemManager>
|
||||
);
|
||||
})}
|
||||
</ion-router-outlet>
|
||||
);
|
||||
}
|
||||
|
||||
static get contextType() {
|
||||
return RouteManagerContext;
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
import { Location as HistoryLocation } from 'history';
|
||||
import { ViewItem } from './ViewItem';
|
||||
import { IonRouteData } from './IonRouteData';
|
||||
import { matchPath } from 'react-router-dom';
|
||||
|
||||
export interface ViewStack {
|
||||
id: string;
|
||||
routerOutlet: HTMLIonRouterOutletElement;
|
||||
views: ViewItem[]
|
||||
}
|
||||
|
||||
/**
|
||||
* The holistic view of all the Routes configured for an application inside of an IonRouterOutlet.
|
||||
*/
|
||||
export class ViewStacks {
|
||||
private viewStacks: { [key: string]: ViewStack } = {};
|
||||
|
||||
get(key: string) {
|
||||
return this.viewStacks[key];
|
||||
}
|
||||
|
||||
set(key: string, viewStack: ViewStack) {
|
||||
this.viewStacks[key] = viewStack;
|
||||
}
|
||||
|
||||
getKeys() {
|
||||
return Object.keys(this.viewStacks);
|
||||
}
|
||||
|
||||
delete(key: string) {
|
||||
delete this.viewStacks[key];
|
||||
}
|
||||
|
||||
findViewInfoByLocation(location: HistoryLocation, key?: string) {
|
||||
let view: ViewItem<IonRouteData> | undefined;
|
||||
let match: IonRouteData["match"] | null | undefined;
|
||||
let viewStack: ViewStack | undefined;
|
||||
if (key) {
|
||||
viewStack = this.viewStacks[key];
|
||||
if (viewStack) {
|
||||
viewStack.views.some(matchView);
|
||||
}
|
||||
} else {
|
||||
const keys = this.getKeys();
|
||||
keys.some(key => {
|
||||
viewStack = this.viewStacks[key];
|
||||
return viewStack.views.some(matchView);
|
||||
});
|
||||
}
|
||||
|
||||
const result = { view, viewStack, match };
|
||||
return result;
|
||||
|
||||
function matchView(v: ViewItem) {
|
||||
const matchProps = {
|
||||
exact: v.routeData.childProps.exact,
|
||||
path: v.routeData.childProps.path || v.routeData.childProps.from,
|
||||
component: v.routeData.childProps.component
|
||||
};
|
||||
match = matchPath(location.pathname, matchProps)
|
||||
if (match) {
|
||||
view = v;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
findViewInfoById(id: string = '') {
|
||||
let view: ViewItem<IonRouteData> | undefined;
|
||||
let viewStack: ViewStack | undefined;
|
||||
const keys = this.getKeys();
|
||||
keys.some(key => {
|
||||
const vs = this.viewStacks[key];
|
||||
view = vs.views.find(x => x.id === id);
|
||||
if (view) {
|
||||
viewStack = vs;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
return { view, viewStack };
|
||||
}
|
||||
|
||||
setHiddenViews() {
|
||||
const keys = this.getKeys();
|
||||
keys.forEach(key => {
|
||||
const viewStack = this.viewStacks[key];
|
||||
viewStack.views.forEach(view => {
|
||||
if(!view.routeData.match && !view.isIonRoute) {
|
||||
view.show = false;
|
||||
view.mount = false;
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -2,21 +2,24 @@ import React from 'react';
|
|||
import { IonLifeCycleContext, DefaultIonLifeCycleContext } from '@ionic/react';
|
||||
import { RouteManagerContext } from './RouteManagerContext';
|
||||
|
||||
interface StackItemManagerProps {
|
||||
interface ViewTransitionManagerProps {
|
||||
id: string;
|
||||
mount: boolean;
|
||||
}
|
||||
|
||||
interface StackItemManagerState {
|
||||
interface ViewTransitionManagerState {
|
||||
show: boolean;
|
||||
}
|
||||
|
||||
export class ViewItemManager extends React.Component<StackItemManagerProps, StackItemManagerState> {
|
||||
/**
|
||||
* Manages the View's DOM lifetime by keeping it around long enough to complete page transitions before removing it.
|
||||
*/
|
||||
export class ViewTransitionManager extends React.Component<ViewTransitionManagerProps, ViewTransitionManagerState> {
|
||||
ionLifeCycleContext = new DefaultIonLifeCycleContext();
|
||||
_isMounted = false;
|
||||
context!: React.ContextType<typeof RouteManagerContext>;
|
||||
|
||||
constructor(props: StackItemManagerProps) {
|
||||
constructor(props: ViewTransitionManagerProps) {
|
||||
super(props)
|
||||
this.state = {
|
||||
show: true
|
|
@ -1,3 +1,3 @@
|
|||
export * from './Router';
|
||||
export * from './ViewManager';
|
||||
|
||||
export { IonReactRouter } from './Router';
|
||||
export { ViewManager } from './ViewManager';
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
export const isDevMode = () => {
|
||||
return process && process.env && process.env.NODE_ENV === 'development';
|
||||
};
|
||||
|
||||
export const deprecationWarning = (message: string) => {
|
||||
if(isDevMode()) {
|
||||
console.warn(message);
|
||||
}
|
||||
};
|
|
@ -1 +1,2 @@
|
|||
export * from './generateUniqueId';
|
||||
export * from './dev';
|
||||
|
|
|
@ -1,3 +1,62 @@
|
|||
|
||||
# [4.8.0-rc2]
|
||||
|
||||
## Features
|
||||
|
||||
Dependencies upgraded to work with Ionic Core 4.9
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
IonPage should be root page for Ionic Pages - fixes [#19146](https://github.com/ionic-team/ionic/issues/19146)
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
### IonPage should be the parent component of Ionic Pages
|
||||
|
||||
`IonPage` is a specialized component that is meant to be the parent container for an Ionic Page, which typically consists of `IonHeader` and `IonContent` components. Previous to RC3, the `@ionic/react-router` library would add this component itself, but this caused some confusion around how `IonPage` should be used and why it was even available. Devs would sometimes include `IonPage` themselves as the parent of their pages, but this could cause issues with page transitions.
|
||||
|
||||
To clear up this confusion, we decided to stop adding the `IonPage` component in the framework and offer guidance that devs should use `IonPage` as the base component in their Ionic Pages.
|
||||
|
||||
Apps migrating from a previous version to RC2 will need to make sure `IonPage` is used as the root component for every Ionic page. Ionic Pages typically have `IonHeader` and `IonContent` tags and are the components rendered by the routes. The starters used a React Fragment before, and this fragment needs to be changed to `IonPage`. Any other uses of `IonPage` should probably be removed.
|
||||
|
||||
Example:
|
||||
|
||||
An Ionic Page before RC2 most likely has a React fragment as its root:
|
||||
|
||||
``` jsx
|
||||
<>
|
||||
<IonHeader>/* header stuff */</IonHeader>
|
||||
<IonContent>/* content stuff */</IonContent>
|
||||
</>
|
||||
```
|
||||
|
||||
In RC2 and greater, the fragment should be updated to an IonPage component:
|
||||
|
||||
``` jsx
|
||||
<IonPage>
|
||||
<IonHeader>/* header stuff */</IonHeader>
|
||||
<IonContent>/* content stuff */</IonContent>
|
||||
</IonPage>
|
||||
```
|
||||
|
||||
### ViewManager
|
||||
|
||||
The `<ViewManager />` component is no longer needed and can be removed. The views inside of an `<IonRouterOutlet>` are now managed by an internal stack manager and don't need the explicit ViewManager anymore.
|
||||
|
||||
`ViewManager` is now a noop component and shows a warning message while in development. `ViewManager` will be removed in the final release of @ionic/react.
|
||||
|
||||
## Other Changes
|
||||
|
||||
### Tab Route Setup
|
||||
|
||||
Prior to RC2, tabs had to specify a tab name in their routes which helped with the page transitions. This was set up in the form of a regex named parameter like so:
|
||||
|
||||
`<Route path="/:tab(tab1)" component={Tab1} exact={true} />`
|
||||
|
||||
This is no longer a requirement. Routes setup this way will continue to work, but the complexity in the path can be removed and updated to:
|
||||
|
||||
`<Route path="/tab1" component={Tab1} exact={true} />`
|
||||
|
||||
# [4.8.0-rc1]
|
||||
|
||||
## Features
|
||||
|
@ -6,6 +65,8 @@ Dependencies upgraded to work with Ionic Core 4.8
|
|||
|
||||
## Bug Fixes
|
||||
|
||||
Attributes applied to Ionic Components now show up in the DOM and are visible in devtools
|
||||
|
||||
`IonBackButton` now preserves the query string parameters from the referring page: https://github.com/ionic-team/ionic/pull/19150/commits/2e2068ee5ea467636b4454e1106df1d8f05230d2
|
||||
|
||||
Supporting class attributes as well as className attributes for Ionic React components: https://github.com/ionic-team/ionic/pull/19150/commits/bef7bc8b1b190bf704fd6960711672bfd7aedfa6, closes [#19107](https://github.com/ionic-team/ionic/issues/19107)
|
||||
|
|
|
@ -45,6 +45,7 @@ To open your application to build/emulate in Android Studio or Xcode run the `op
|
|||
ionic capacitor open <android|ios>
|
||||
```
|
||||
|
||||
|
||||
## Related
|
||||
|
||||
* [Ionic Documentation](https://ionicframework.com/docs/)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@ionic/react",
|
||||
"version": "4.8.0-rc.1",
|
||||
"version": "4.9.0-rc.2",
|
||||
"description": "React specific wrapper for @ionic/core",
|
||||
"keywords": [
|
||||
"ionic",
|
||||
|
@ -38,7 +38,7 @@
|
|||
"css/"
|
||||
],
|
||||
"dependencies": {
|
||||
"@ionic/core": "4.8.0",
|
||||
"@ionic/core": "^4.9.0",
|
||||
"tslib": "*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
@ -48,8 +48,8 @@
|
|||
"devDependencies": {
|
||||
"@types/jest": "^23.3.9",
|
||||
"@types/node": "10.12.9",
|
||||
"@types/react": "^16.9.1",
|
||||
"@types/react-dom": "^16.8.5",
|
||||
"@types/react": "^16.9.2",
|
||||
"@types/react-dom": "^16.9.0",
|
||||
"fs-extra": "^8.1.0",
|
||||
"jest": "^24.8.0",
|
||||
"jest-dom": "^3.4.0",
|
||||
|
|
|
@ -1,25 +1,36 @@
|
|||
import React from 'react';
|
||||
|
||||
import { createForwardRef } from './utils';
|
||||
import { NavContext } from '../contexts/NavContext';
|
||||
|
||||
type Props = React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>;
|
||||
import { ReactProps } from './ReactProps';
|
||||
|
||||
type InternalProps = Props & {
|
||||
forwardedRef?: React.Ref<HTMLDivElement>
|
||||
};
|
||||
export const IonPage = /*@__PURE__*/(() => class IonPageInternal extends React.Component<React.HTMLAttributes<HTMLElement> & ReactProps> {
|
||||
context!: React.ContextType<typeof NavContext>;
|
||||
ref = React.createRef<HTMLDivElement>();
|
||||
|
||||
type ExternalProps = Props & {
|
||||
ref?: React.Ref<HTMLDivElement>
|
||||
};
|
||||
componentDidMount() {
|
||||
if (this.context && this.ref.current) {
|
||||
if (this.context.hasIonicRouter()) {
|
||||
this.context.registerIonPage(this.ref.current);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const IonPageInternal: React.FC<InternalProps> = ({ children, forwardedRef, className, ...props }) => (
|
||||
<div
|
||||
className={className !== undefined ? `ion-page ${className}` : 'ion-page'}
|
||||
ref={forwardedRef}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
render() {
|
||||
const { className, children, ...props } = this.props;
|
||||
|
||||
export const IonPage = /*@__PURE__*/createForwardRef<ExternalProps, HTMLDivElement>(IonPageInternal, 'IonPage');
|
||||
return (
|
||||
<div className={className ? `ion-page ${className}` : 'ion-page'} ref={this.ref} {...props}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
static get displayName() {
|
||||
return 'IonPage';
|
||||
}
|
||||
|
||||
static get contextType() {
|
||||
return NavContext;
|
||||
}
|
||||
})();
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
|
||||
import { JSX as LocalJSX } from '@ionic/core';
|
||||
import React from 'react';
|
||||
|
||||
import { NavContext } from '../contexts/NavContext';
|
||||
|
||||
import { ReactProps } from './ReactProps';
|
||||
import { IonRouterOutletInner } from './inner-proxies';
|
||||
import { createForwardRef } from './utils';
|
||||
|
||||
type Props = LocalJSX.IonRouterOutlet & {
|
||||
ref?: React.RefObject<any>;
|
||||
};
|
||||
|
||||
type InternalProps = Props & {
|
||||
forwardedRef: any;
|
||||
};
|
||||
|
||||
const IonRouterOutletContainer = /*@__PURE__*/(() => class extends React.Component<InternalProps> {
|
||||
context!: React.ContextType<typeof NavContext>;
|
||||
|
||||
render() {
|
||||
|
||||
const StackManager = this.context.getStackManager();
|
||||
|
||||
return (
|
||||
this.context.hasIonicRouter() ? (
|
||||
<StackManager>
|
||||
<IonRouterOutletInner ref={this.props.forwardedRef} {...this.props}>
|
||||
{this.props.children}
|
||||
</IonRouterOutletInner>
|
||||
</StackManager>
|
||||
) : (
|
||||
<IonRouterOutletInner ref={this.props.forwardedRef} {...this.props}>
|
||||
{this.props.children}
|
||||
</IonRouterOutletInner>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
static get contextType() {
|
||||
return NavContext;
|
||||
}
|
||||
})();
|
||||
|
||||
export const IonRouterOutlet = createForwardRef<Props & ReactProps, HTMLIonRouterOutletElement>(IonRouterOutletContainer, 'IonRouterOutlet');
|
|
@ -1,10 +1,10 @@
|
|||
import { RouterDirection } from '@ionic/core';
|
||||
import React from 'react';
|
||||
import ReactDom from 'react-dom';
|
||||
|
||||
import { NavContext } from '../contexts/NavContext';
|
||||
|
||||
import { ReactProps } from './ReactProps';
|
||||
import { RouterDirection } from './hrefprops';
|
||||
import { attachEventProps, createForwardRef, dashToPascalCase, isCoveredByReact } from './utils';
|
||||
|
||||
interface IonicReactInternalProps<ElementType> {
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
export declare type RouterDirection = 'forward' | 'back' | 'none';
|
||||
|
||||
export type HrefProps<T> = Omit<T, 'routerDirection'> & {
|
||||
routerDirection?: RouterDirection;
|
||||
};
|
|
@ -20,9 +20,11 @@ export { IonPage } from './IonPage';
|
|||
export { IonTabs } from './navigation/IonTabs';
|
||||
export { IonTabBar } from './navigation/IonTabBar';
|
||||
export { IonBackButton } from './navigation/IonBackButton';
|
||||
export { IonRouterOutlet } from './IonRouterOutlet';
|
||||
|
||||
// Utils
|
||||
export { isPlatform, getPlatforms } from './utils';
|
||||
export { RouterDirection } from './hrefprops';
|
||||
|
||||
// Icons that are used by internal components
|
||||
addIcons({
|
||||
|
|
|
@ -4,3 +4,4 @@ import { /*@__PURE__*/ createReactComponent } from './createComponent';
|
|||
|
||||
export const IonTabBarInner = /*@__PURE__*/createReactComponent<JSX.IonTabBar, HTMLIonTabBarElement>('ion-tab-bar');
|
||||
export const IonBackButtonInner = /*@__PURE__*/createReactComponent<JSX.IonBackButton, HTMLIonBackButtonElement>('ion-back-button');
|
||||
export const IonRouterOutletInner = /*@__PURE__*/createReactComponent<JSX.IonRouterOutlet, HTMLIonRouterOutletElement>('ion-router-outlet');
|
||||
|
|
|
@ -2,9 +2,10 @@ import { JSX as LocalJSX } from '@ionic/core';
|
|||
import React from 'react';
|
||||
|
||||
import { NavContext } from '../../contexts/NavContext';
|
||||
import { ReactProps } from '../ReactProps';
|
||||
import { IonBackButtonInner } from '../inner-proxies';
|
||||
|
||||
type Props = LocalJSX.IonBackButton & {
|
||||
type Props = LocalJSX.IonBackButton & ReactProps & {
|
||||
ref?: React.RefObject<HTMLIonBackButtonElement>;
|
||||
};
|
||||
|
||||
|
@ -13,13 +14,11 @@ export const IonBackButton = /*@__PURE__*/(() => class extends React.Component<P
|
|||
|
||||
clickButton = (e: MouseEvent) => {
|
||||
const defaultHref = this.props.defaultHref;
|
||||
if (defaultHref !== undefined) {
|
||||
if (this.context.hasIonicRouter()) {
|
||||
e.stopPropagation();
|
||||
this.context.goBack(defaultHref);
|
||||
} else {
|
||||
window.location.href = defaultHref;
|
||||
}
|
||||
if (this.context.hasIonicRouter()) {
|
||||
e.stopPropagation();
|
||||
this.context.goBack(defaultHref);
|
||||
} else if (defaultHref !== undefined) {
|
||||
window.location.href = defaultHref;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import { IonTabButton } from '../proxies';
|
|||
|
||||
type Props = LocalJSX.IonTabBar & {
|
||||
ref?: React.RefObject<HTMLIonTabBarElement>;
|
||||
navigate: (path: string) => void;
|
||||
navigate: (path: string, direction: 'back' | 'none') => void;
|
||||
currentPath: string;
|
||||
children?: React.ReactNode;
|
||||
};
|
||||
|
@ -68,10 +68,11 @@ const IonTabBarUnwrapped = /*@__PURE__*/(() => class extends React.Component<Pro
|
|||
}
|
||||
|
||||
private onTabButtonClick = (e: CustomEvent<{ href: string, selected: boolean, tab: string }>) => {
|
||||
const targetUrl = (this.state.activeTab === e.detail.tab) ?
|
||||
this.state.tabs[e.detail.tab].originalHref :
|
||||
this.state.tabs[e.detail.tab].currentHref;
|
||||
this.props.navigate(targetUrl);
|
||||
if (this.state.activeTab === e.detail.tab) {
|
||||
this.props.navigate(this.state.tabs[e.detail.tab].originalHref, 'back');
|
||||
} else {
|
||||
this.props.navigate(this.state.tabs[e.detail.tab].currentHref, 'none');
|
||||
}
|
||||
}
|
||||
|
||||
private renderChild = (activeTab: string | null | undefined) => (child: (React.ReactElement<LocalJSX.IonTabButton & { onIonTabButtonClick: (e: CustomEvent) => void }>) | null | undefined) => {
|
||||
|
@ -100,8 +101,8 @@ export const IonTabBar: React.FC<LocalJSX.IonTabBar & { currentPath?: string, na
|
|||
return (
|
||||
<IonTabBarUnwrapped
|
||||
{...props as any}
|
||||
navigate={props.navigate || ((path: string) => {
|
||||
context.navigate(path);
|
||||
navigate={props.navigate || ((path: string, direction: 'back' | 'none') => {
|
||||
context.navigate(path, direction);
|
||||
})}
|
||||
currentPath={props.currentPath || context.currentPath}
|
||||
>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
|
||||
import { NavContext } from '../../contexts/NavContext';
|
||||
import { IonRouterOutlet } from '../proxies';
|
||||
import { IonRouterOutlet } from '../IonRouterOutlet';
|
||||
|
||||
import { IonTabBar } from './IonTabBar';
|
||||
|
||||
|
@ -60,19 +60,11 @@ export const IonTabs = /*@__PURE__*/(() => class extends React.Component<Props>
|
|||
throw new Error('IonTabs needs a IonTabBar');
|
||||
}
|
||||
|
||||
const NavManager = this.context.getViewManager();
|
||||
|
||||
return (
|
||||
<div style={hostStyles}>
|
||||
{tabBar.props.slot === 'top' ? tabBar : null}
|
||||
<div style={tabsInner} className="tabs-inner">
|
||||
{this.context.hasIonicRouter() ? (
|
||||
<NavManager>
|
||||
{outlet}
|
||||
</NavManager>
|
||||
) : (
|
||||
<>{outlet}</>
|
||||
)}
|
||||
{outlet}
|
||||
</div>
|
||||
{tabBar.props.slot === 'bottom' ? tabBar : null}
|
||||
</div>
|
||||
|
|
|
@ -2,6 +2,7 @@ import { JSX } from '@ionic/core';
|
|||
import { JSX as IoniconsJSX } from 'ionicons';
|
||||
|
||||
import { createReactComponent } from './createComponent';
|
||||
import { HrefProps } from './hrefprops';
|
||||
|
||||
// ionicons
|
||||
export const IonIcon = /*@__PURE__*/createReactComponent<IoniconsJSX.IonIcon, HTMLIonIconElement>('ion-icon');
|
||||
|
@ -10,13 +11,13 @@ export const IonIcon = /*@__PURE__*/createReactComponent<IoniconsJSX.IonIcon, HT
|
|||
export const IonApp = /*@__PURE__*/createReactComponent<JSX.IonApp, HTMLIonAppElement>('ion-app');
|
||||
export const IonTab = /*@__PURE__*/createReactComponent<JSX.IonTab, HTMLIonTabElement>('ion-tab');
|
||||
export const IonTabButton = /*@__PURE__*/createReactComponent<JSX.IonTabButton, HTMLIonTabButtonElement>('ion-tab-button');
|
||||
export const IonRouterLink = /*@__PURE__*/createReactComponent<JSX.IonRouterLink, HTMLIonRouterLinkElement>('ion-router-link', true);
|
||||
export const IonRouterLink = /*@__PURE__*/createReactComponent<HrefProps<JSX.IonRouterLink>, HTMLIonRouterLinkElement>('ion-router-link', true);
|
||||
export const IonAvatar = /*@__PURE__*/createReactComponent<JSX.IonAvatar, HTMLIonAvatarElement>('ion-avatar');
|
||||
export const IonBackdrop = /*@__PURE__*/createReactComponent<JSX.IonBackdrop, HTMLIonBackdropElement>('ion-backdrop');
|
||||
export const IonBadge = /*@__PURE__*/createReactComponent<JSX.IonBadge, HTMLIonBadgeElement>('ion-badge');
|
||||
export const IonButton = /*@__PURE__*/createReactComponent<JSX.IonButton, HTMLIonButtonElement>('ion-button', true);
|
||||
export const IonButton = /*@__PURE__*/createReactComponent<HrefProps<JSX.IonButton>, HTMLIonButtonElement>('ion-button', true);
|
||||
export const IonButtons = /*@__PURE__*/createReactComponent<JSX.IonButtons, HTMLIonButtonsElement>('ion-buttons');
|
||||
export const IonCard = /*@__PURE__*/createReactComponent<JSX.IonCard, HTMLIonCardElement>('ion-card', true);
|
||||
export const IonCard = /*@__PURE__*/createReactComponent<HrefProps<JSX.IonCard>, HTMLIonCardElement>('ion-card', true);
|
||||
export const IonCardContent = /*@__PURE__*/createReactComponent<JSX.IonCardContent, HTMLIonCardContentElement>('ion-card-content');
|
||||
export const IonCardHeader = /*@__PURE__*/createReactComponent<JSX.IonCardHeader, HTMLIonCardHeaderElement>('ion-card-header');
|
||||
export const IonCardSubtitle = /*@__PURE__*/createReactComponent<JSX.IonCardSubtitle, HTMLIonCardSubtitleElement>('ion-card-subtitle');
|
||||
|
@ -27,7 +28,7 @@ export const IonContent = /*@__PURE__*/createReactComponent<JSX.IonContent, HTML
|
|||
export const IonChip = /*@__PURE__*/createReactComponent<JSX.IonChip, HTMLIonChipElement>('ion-chip');
|
||||
export const IonDatetime = /*@__PURE__*/createReactComponent<JSX.IonDatetime, HTMLIonDatetimeElement>('ion-datetime');
|
||||
export const IonFab = /*@__PURE__*/createReactComponent<JSX.IonFab, HTMLIonFabElement>('ion-fab');
|
||||
export const IonFabButton = /*@__PURE__*/createReactComponent<JSX.IonFabButton, HTMLIonFabButtonElement>('ion-fab-button', true);
|
||||
export const IonFabButton = /*@__PURE__*/createReactComponent<HrefProps<JSX.IonFabButton>, HTMLIonFabButtonElement>('ion-fab-button', true);
|
||||
export const IonFabList = /*@__PURE__*/createReactComponent<JSX.IonFabList, HTMLIonFabListElement>('ion-fab-list');
|
||||
export const IonFooter = /*@__PURE__*/createReactComponent<JSX.IonFooter, HTMLIonFooterElement>('ion-footer');
|
||||
export const IonGrid = /*@__PURE__*/createReactComponent<JSX.IonGrid, HTMLIonGridElement>('ion-grid');
|
||||
|
@ -36,10 +37,10 @@ export const IonImg = /*@__PURE__*/createReactComponent<JSX.IonImg, HTMLIonImgEl
|
|||
export const IonInfiniteScroll = /*@__PURE__*/createReactComponent<JSX.IonInfiniteScroll, HTMLIonInfiniteScrollElement>('ion-infinite-scroll');
|
||||
export const IonInfiniteScrollContent = /*@__PURE__*/createReactComponent<JSX.IonInfiniteScrollContent, HTMLIonInfiniteScrollContentElement>('ion-infinite-scroll-content');
|
||||
export const IonInput = /*@__PURE__*/createReactComponent<JSX.IonInput, HTMLIonInputElement>('ion-input');
|
||||
export const IonItem = /*@__PURE__*/createReactComponent<JSX.IonItem, HTMLIonItemElement>('ion-item', true);
|
||||
export const IonItem = /*@__PURE__*/createReactComponent<HrefProps<JSX.IonItem>, HTMLIonItemElement>('ion-item', true);
|
||||
export const IonItemDivider = /*@__PURE__*/createReactComponent<JSX.IonItemDivider, HTMLIonItemDividerElement>('ion-item-divider');
|
||||
export const IonItemGroup = /*@__PURE__*/createReactComponent<JSX.IonItemGroup, HTMLIonItemGroupElement>('ion-item-group');
|
||||
export const IonItemOption = /*@__PURE__*/createReactComponent<JSX.IonItemOption, HTMLIonItemOptionElement>('ion-item-option', true);
|
||||
export const IonItemOption = /*@__PURE__*/createReactComponent<HrefProps<JSX.IonItemOption>, HTMLIonItemOptionElement>('ion-item-option', true);
|
||||
export const IonItemOptions = /*@__PURE__*/createReactComponent<JSX.IonItemOptions, HTMLIonItemOptionsElement>('ion-item-options');
|
||||
export const IonItemSliding = /*@__PURE__*/createReactComponent<JSX.IonItemSliding, HTMLIonItemSlidingElement>('ion-item-sliding');
|
||||
export const IonLabel = /*@__PURE__*/createReactComponent<JSX.IonLabel, HTMLIonLabelElement>('ion-label');
|
||||
|
@ -61,7 +62,6 @@ export const IonRefresherContent = /*@__PURE__*/createReactComponent<JSX.IonRefr
|
|||
export const IonReorder = /*@__PURE__*/createReactComponent<JSX.IonReorder, HTMLIonReorderElement>('ion-reorder');
|
||||
export const IonReorderGroup = /*@__PURE__*/createReactComponent<JSX.IonReorderGroup, HTMLIonReorderGroupElement>('ion-reorder-group');
|
||||
export const IonRippleEffect = /*@__PURE__*/createReactComponent<JSX.IonRippleEffect, HTMLIonRippleEffectElement>('ion-ripple-effect');
|
||||
export const IonRouterOutlet = /*@__PURE__*/createReactComponent<JSX.IonRouterOutlet, HTMLIonRouterOutletElement>('ion-router-outlet');
|
||||
export const IonRow = /*@__PURE__*/createReactComponent<JSX.IonRow, HTMLIonRowElement>('ion-row');
|
||||
export const IonSearchbar = /*@__PURE__*/createReactComponent<JSX.IonSearchbar, HTMLIonSearchbarElement>('ion-searchbar');
|
||||
export const IonSegment = /*@__PURE__*/createReactComponent<JSX.IonSegment, HTMLIonSegmentElement>('ion-segment');
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import { camelToDashCase } from './case';
|
||||
|
||||
export const attachEventProps = (node: HTMLElement, newProps: any, oldProps: any = {}) => {
|
||||
// add any classes in className to the class list
|
||||
const className = getClassName(node.classList, newProps, oldProps);
|
||||
|
@ -6,7 +8,7 @@ export const attachEventProps = (node: HTMLElement, newProps: any, oldProps: any
|
|||
}
|
||||
|
||||
Object.keys(newProps).forEach(name => {
|
||||
if (name === 'children' || name === 'style' || name === 'ref' || name === 'className') {
|
||||
if (name === 'children' || name === 'style' || name === 'ref' || name === 'class' || name === 'className' || name === 'forwardedRef') {
|
||||
return;
|
||||
}
|
||||
if (name.indexOf('on') === 0 && name[2] === name[2].toUpperCase()) {
|
||||
|
@ -17,7 +19,11 @@ export const attachEventProps = (node: HTMLElement, newProps: any, oldProps: any
|
|||
syncEvent(node, eventNameLc, newProps[name]);
|
||||
}
|
||||
} else {
|
||||
(node as any)[name] = newProps[name];
|
||||
if (typeof newProps[name] === 'object') {
|
||||
(node as any)[name] = newProps[name];
|
||||
} else {
|
||||
node.setAttribute(camelToDashCase(name), newProps[name]);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
export const dashToPascalCase = (str: string) => str.toLowerCase().split('-').map(segment => segment.charAt(0).toUpperCase() + segment.slice(1)).join('');
|
||||
export const camelToDashCase = (str: string) => str.replace(/([A-Z])/g, (m: string) => `-${m[0].toLowerCase()}`);
|
|
@ -1,6 +1,5 @@
|
|||
import { Platforms, getPlatforms as getPlatformsCore, isPlatform as isPlatformCore } from '@ionic/core';
|
||||
import React from 'react';
|
||||
export const dashToPascalCase = (str: string) => str.toLowerCase().split('-').map(segment => segment.charAt(0).toUpperCase() + segment.slice(1)).join('');
|
||||
|
||||
export type IonicReactExternalProps<PropType, ElementType> = PropType & {
|
||||
ref?: React.RefObject<ElementType>;
|
||||
|
@ -17,6 +16,7 @@ export const createForwardRef = <PropType, ElementType>(ReactComponent: any, dis
|
|||
};
|
||||
|
||||
export * from './attachEventProps';
|
||||
export * from './case';
|
||||
|
||||
export const isPlatform = (platform: Platforms) => {
|
||||
return isPlatformCore(window, platform);
|
||||
|
|
|
@ -4,17 +4,20 @@ import React from 'react';
|
|||
export interface NavContextState {
|
||||
getHistory: () => History;
|
||||
getLocation: () => Location;
|
||||
getViewManager: () => any;
|
||||
getPageManager: () => any;
|
||||
getStackManager: () => any;
|
||||
goBack: (defaultHref?: string) => void;
|
||||
navigate: (path: string, direction?: RouterDirection) => void;
|
||||
navigate: (path: string, direction?: RouterDirection | 'none') => void;
|
||||
hasIonicRouter: () => boolean;
|
||||
registerIonPage: (page: HTMLElement) => void;
|
||||
currentPath: string | undefined;
|
||||
}
|
||||
|
||||
export const NavContext = /*@__PURE__*/React.createContext<NavContextState>({
|
||||
getHistory: () => window.history,
|
||||
getLocation: () => window.location,
|
||||
getViewManager: () => undefined,
|
||||
getPageManager: () => undefined,
|
||||
getStackManager: () => undefined,
|
||||
goBack: (defaultHref?: string) => {
|
||||
if (defaultHref !== undefined) {
|
||||
window.location.pathname = defaultHref;
|
||||
|
@ -24,5 +27,6 @@ export const NavContext = /*@__PURE__*/React.createContext<NavContextState>({
|
|||
},
|
||||
navigate: (path: string) => { window.location.pathname = path; },
|
||||
hasIonicRouter: () => false,
|
||||
registerIonPage: () => undefined,
|
||||
currentPath: undefined
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue