Skip to content

Commit 11fda41

Browse files
authored
feat(slides): add IonicSlides module for Swiper migration, deprecate ion-slides (ionic-team#23844)
backports ionic-team#23447
1 parent 33f0bcd commit 11fda41

8 files changed

Lines changed: 282 additions & 1 deletion

File tree

angular/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export * from './types/ionic-lifecycle-hooks';
4343
export { IonicModule } from './ionic-module';
4444

4545
// UTILS
46-
export { IonicSafeString, getPlatforms, isPlatform, createAnimation } from '@ionic/core';
46+
export { IonicSafeString, getPlatforms, isPlatform, createAnimation, IonicSwiper } from '@ionic/core';
4747

4848
// CORE TYPES
4949
export {
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
import { addEventListener, raf, removeEventListener } from '../../utils/helpers';
2+
3+
/**
4+
* This is a plugin for Swiper that allows it to work
5+
* with Ionic Framework and the routing integrations.
6+
* Without this plugin, Swiper would be incapable of correctly
7+
* determining the dimensions of the slides component as
8+
* each view is initially hidden before transitioning in.
9+
*/
10+
const setupSwiperInIonic = (swiper: any, watchForIonPageChanges = true) => {
11+
if (typeof (window as any) === 'undefined') { return; }
12+
13+
const swiperEl = swiper.el;
14+
const ionPage = swiperEl.closest('.ion-page');
15+
16+
if (!ionPage) {
17+
if (watchForIonPageChanges) {
18+
19+
/**
20+
* If no ion page found, it is possible
21+
* that we are in the overlay setup step
22+
* where the inner component has been
23+
* created but not attached to the DOM yet.
24+
* If so, wait for the .ion-page class to
25+
* appear on the root div and re-run setup.
26+
*/
27+
const rootNode = swiperEl.getRootNode();
28+
if (rootNode.tagName === 'DIV') {
29+
const mo = new MutationObserver((m: MutationRecord[]) => {
30+
const mutation = m[0];
31+
const wasEmpty = mutation.oldValue === null;
32+
const hasIonPage = rootNode.classList.contains('ion-page');
33+
34+
/**
35+
* Now that we have an .ion-page class
36+
* we can safely attempt setup again.
37+
*/
38+
if (wasEmpty && hasIonPage) {
39+
mo.disconnect();
40+
41+
/**
42+
* Set false here so we do not
43+
* get infinite loops
44+
*/
45+
setupSwiperInIonic(swiper, false);
46+
}
47+
});
48+
49+
mo.observe(rootNode, {
50+
attributeFilter: ['class'],
51+
attributeOldValue: true
52+
});
53+
}
54+
}
55+
return;
56+
}
57+
58+
/**
59+
* If using slides in a modal or
60+
* popover we need to wait for the
61+
* overlay to be shown as these components
62+
* are hidden when they are initially created.
63+
*/
64+
const modalOrPopover = swiperEl.closest('ion-modal, ion-popover');
65+
if (modalOrPopover) {
66+
const eventName = modalOrPopover.tagName === 'ION-MODAL' ? 'ionModalWillPresent' : 'ionPopoverWillPresent';
67+
const overlayCallback = () => {
68+
/**
69+
* We need an raf here so the update
70+
* is fired one tick after the overlay is shown.
71+
*/
72+
raf(() => {
73+
swiperEl.swiper.update();
74+
removeEventListener(modalOrPopover, eventName, overlayCallback);
75+
});
76+
}
77+
addEventListener(modalOrPopover, eventName, overlayCallback);
78+
} else {
79+
/**
80+
* If using slides in a page
81+
* we need to wait for the ion-page-invisible
82+
* class to be removed so Swiper can correctly
83+
* compute the dimensions of the slides.
84+
*/
85+
const mo = new MutationObserver((m: MutationRecord[]) => {
86+
const mutation = m[0];
87+
const wasPageHidden = mutation.oldValue?.includes('ion-page-invisible');
88+
const isPageHidden = ionPage.classList.contains('ion-page-invisible');
89+
90+
/**
91+
* Only update Swiper if the page was
92+
* hidden but is no longer hidden.
93+
*/
94+
if (!isPageHidden && isPageHidden !== wasPageHidden) {
95+
swiperEl.swiper.update();
96+
}
97+
});
98+
99+
mo.observe(ionPage, {
100+
attributeFilter: ['class'],
101+
attributeOldValue: true
102+
});
103+
}
104+
105+
/**
106+
* We also need to listen for the appload event
107+
* which is emitted by Stencil in the
108+
* event that Swiper is being used on the
109+
* view that is rendered initially.
110+
*/
111+
const onAppLoad = () => {
112+
swiperEl.swiper.update();
113+
removeEventListener(window, 'appload', onAppLoad);
114+
}
115+
116+
addEventListener(window, 'appload', onAppLoad);
117+
}
118+
119+
export const IonicSwiper = {
120+
name: 'ionic',
121+
on: {
122+
afterInit(swiper: any) {
123+
setupSwiperInIonic(swiper);
124+
}
125+
}
126+
}

core/src/components/slides/readme.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
The Slides component is a multi-section container. Each section can be swiped
44
or dragged between. It contains any number of [Slide](../slide) components.
55

6+
This guide will cover migration from the deprecated `ion-slides` component to the framework-specific solutions that Swiper.js provides as well as the existing `ion-slides` API for developers who are still using that component.
67

78
Adopted from Swiper.js:
89
The most modern mobile touch slider and framework with hardware accelerated transitions.
@@ -15,6 +16,42 @@ http://www.idangero.us/
1516

1617
Licensed under MIT
1718

19+
## Migration
20+
21+
With the release of Ionic Framework v6, the Ionic Team has deprecated the `ion-slides` and `ion-slide` components in favor of using the official framework integrations provided by Swiper. Fear not! You will still be able to use slides components in your application. Additionally, because you are still using Swiper, the functionality of your slides component should remain exactly the same.
22+
23+
### What is Swiper.js?
24+
25+
Swiper.js is the carousel/slider library that powers `ion-slides`. The library is bundled automatically with all versions of Ionic Framework. When Ionic Framework v4. was first released, Swiper did not have framework specific integrations of its library, so `ion-slides` was created as a way of bridging the gap between the core Swiper library and frameworks such as Angular, React, and Vue.
26+
27+
Since then, the Swiper team has released framework specific integrations of Swiper.js for Angular, React, Vue, and more!
28+
29+
### What are the benefits of this change?
30+
31+
There are several benefits for members of the Ionic Framework community. By using the official Swiper.js framework integrations:
32+
33+
- Developers can now be in control of the exact version of Swiper.js they want to use. Previously, developers would need to rely on the Ionic Team to update the version internally and release a new version of Ionic Framework.
34+
- The Ionic Team can spend more time triaging and fixing other non-slides issues, speeding up our development process so we can make the framework work better for our community.
35+
- Developers should experience fewer bugs.
36+
- Application bundle sizes can shrink in some cases. By installing Swiper.js as a 3rd party dependency in your application, bundlers such as Webpack or Rollup should be able to treeshake your code better.
37+
- Developers have access to new features that they previously did not have when using `ion-slides`.
38+
39+
### How long do I have to migrate?
40+
41+
We plan to remove `ion-slides` and `ion-slide` in Ionic Framework v7. `ion-slides` and `ion-slide` will continue to be available for the entire Ionic Framework v6 lifecycle but will only receive critical bug fixes.
42+
43+
### How do I migrate?
44+
45+
Since the underlying technology that powers your slides is the same, the migration process is easy! Follow the guides below for your specific framework.
46+
47+
Migration for Ionic Angular users: https://ionicframework.com/docs/angular/slides
48+
Migration for Ionic React users: https://ionicframework.com/docs/react/slides
49+
Migration for Ionic Vue users: https://ionicframework.com/docs/vue/slides
50+
51+
------
52+
53+
The following documentation applies to the `ion-slides` component.
54+
1855
## Custom Animations
1956

2057
By default, Ionic slides use the built-in `slide` animation effect. Custom animations can be provided via the `options` property. Examples of other animations can be found below.

core/src/components/slides/slides.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,10 @@ export class Slides implements ComponentInterface {
136136
*/
137137
@Event() ionSlideTouchEnd!: EventEmitter<void>;
138138

139+
componentWillLoad() {
140+
console.warn(`[Deprecation Warning]: ion-slides has been deprecated and will be removed in Ionic Framework v7.0. We recommend using the framework-specific integrations that Swiper.js provides, allowing for faster bug fixes and an improved developer experience. See https://ionicframework.com/docs/api/slides#migration for more information including migration steps.`);
141+
}
142+
139143
connectedCallback() {
140144
// tslint:disable-next-line: strict-type-predicates
141145
if (typeof MutationObserver !== 'undefined') {

core/src/css/ionic-swiper.scss

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
@import "../themes/ionic.skip-warns.scss";
2+
@import "../components/slides/slides.ios.vars.scss";
3+
4+
// Slides
5+
// --------------------------------------------------
6+
7+
.swiper-container {
8+
9+
// These values are the same for iOS and MD
10+
// We just do not add a .md or .ios class beforehand
11+
// so the styles are easier to override by the user.
12+
--bullet-background: #{$slides-ios-bullet-background};
13+
--bullet-background-active: #{$slides-ios-bullet-background-active};
14+
--progress-bar-background: #{$slides-ios-progress-bar-background};
15+
--progress-bar-background-active: #{$slides-ios-progress-bar-background-active};
16+
--scroll-bar-background: #{$slides-ios-scroll-bar-background};
17+
--scroll-bar-background-active: #{$slides-ios-scroll-bar-drag-background};
18+
/**
19+
* @prop --bullet-background: Background of the pagination bullets
20+
* @prop --bullet-background-active: Background of the active pagination bullet
21+
*
22+
* @prop --progress-bar-background: Background of the pagination progress-bar
23+
* @prop --progress-bar-background-active: Background of the active pagination progress-bar
24+
*
25+
* @prop --scroll-bar-background: Background of the pagination scroll-bar
26+
* @prop --scroll-bar-background-active: Background of the active pagination scroll-bar
27+
*/
28+
display: block;
29+
30+
user-select: none;
31+
}
32+
33+
// Pagination Bullets
34+
// --------------------------------------------------
35+
36+
.swiper-pagination-bullet {
37+
background: var(--bullet-background);
38+
}
39+
40+
.swiper-pagination-bullet-active {
41+
background: var(--bullet-background-active);
42+
}
43+
44+
45+
// Pagination Progress Bar
46+
// --------------------------------------------------
47+
48+
.swiper-pagination-progressbar {
49+
background: var(--progress-bar-background);
50+
}
51+
52+
.swiper-pagination-progressbar .swiper-pagination-progressbar-fill {
53+
background: var(--progress-bar-background-active);
54+
}
55+
56+
// Scrollbar
57+
// --------------------------------------------------
58+
59+
.swiper-scrollbar {
60+
background: var(--scroll-bar-background);
61+
}
62+
63+
.swiper-scrollbar-drag {
64+
background: var(--scroll-bar-background-active);
65+
}
66+
67+
// Slide
68+
// --------------------------------------------------
69+
70+
ion-slide {
71+
display: block;
72+
73+
width: 100%;
74+
height: 100%;
75+
}
76+
77+
.slide-zoom {
78+
display: block;
79+
80+
width: 100%;
81+
82+
text-align: center;
83+
}
84+
85+
.swiper-slide {
86+
87+
// Center slide text vertically
88+
display: flex;
89+
position: relative;
90+
91+
flex-shrink: 0;
92+
align-items: center;
93+
justify-content: center;
94+
95+
width: 100%;
96+
height: 100%;
97+
98+
font-size: 18px;
99+
100+
text-align: center;
101+
box-sizing: border-box;
102+
}
103+
104+
.swiper-slide img {
105+
width: auto;
106+
max-width: 100%;
107+
height: auto;
108+
max-height: 100%;
109+
}

core/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,4 @@ export { IonicConfig, getMode, setupConfig } from './utils/config';
1313
export { LIFECYCLE_WILL_ENTER, LIFECYCLE_DID_ENTER, LIFECYCLE_WILL_LEAVE, LIFECYCLE_DID_LEAVE, LIFECYCLE_WILL_UNLOAD } from './components/nav/constants';
1414
export { menuController } from './utils/menu-controller';
1515
export { alertController, actionSheetController, modalController, loadingController, pickerController, popoverController, toastController } from './utils/overlays';
16+
export { IonicSwiper } from './components/slides/IonicSwiper';

packages/react/src/components/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ export {
3333
mdTransitionAnimation,
3434
NavComponentWithProps,
3535
setupConfig,
36+
IonicSwiper,
3637

3738
SpinnerTypes,
3839

packages/vue/src/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ export {
7272
// Hardware Back Button
7373
BackButtonEvent,
7474

75+
// Swiper
76+
IonicSwiper,
77+
7578
SpinnerTypes,
7679

7780
ActionSheetOptions,

0 commit comments

Comments
 (0)