Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(slides): add IonicSlides module for Swiper migration, deprecate ion-slides #23844

Merged
merged 5 commits into from
Aug 31, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion angular/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export * from './types/ionic-lifecycle-hooks';
export { IonicModule } from './ionic-module';

// UTILS
export { IonicSafeString, getPlatforms, isPlatform, createAnimation } from '@ionic/core';
export { IonicSafeString, getPlatforms, isPlatform, createAnimation, IonicSwiper } from '@ionic/core';

// CORE TYPES
export {
Expand Down
126 changes: 126 additions & 0 deletions core/src/components/slides/IonicSwiper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import { addEventListener, raf, removeEventListener } from '../../utils/helpers';

/**
* This is a plugin for Swiper that allows it to work
* with Ionic Framework and the routing integrations.
* Without this plugin, Swiper would be incapable of correctly
* determining the dimensions of the slides component as
* each view is initially hidden before transitioning in.
*/
const setupSwiperInIonic = (swiper: any, watchForIonPageChanges = true) => {
if (typeof (window as any) === 'undefined') { return; }

const swiperEl = swiper.el;
const ionPage = swiperEl.closest('.ion-page');

if (!ionPage) {
if (watchForIonPageChanges) {

/**
* If no ion page found, it is possible
* that we are in the overlay setup step
* where the inner component has been
* created but not attached to the DOM yet.
* If so, wait for the .ion-page class to
* appear on the root div and re-run setup.
*/
const rootNode = swiperEl.getRootNode();
if (rootNode.tagName === 'DIV') {
const mo = new MutationObserver((m: MutationRecord[]) => {
const mutation = m[0];
const wasEmpty = mutation.oldValue === null;
const hasIonPage = rootNode.classList.contains('ion-page');

/**
* Now that we have an .ion-page class
* we can safely attempt setup again.
*/
if (wasEmpty && hasIonPage) {
mo.disconnect();

/**
* Set false here so we do not
* get infinite loops
*/
setupSwiperInIonic(swiper, false);
}
});

mo.observe(rootNode, {
attributeFilter: ['class'],
attributeOldValue: true
});
}
}
return;
}

/**
* If using slides in a modal or
* popover we need to wait for the
* overlay to be shown as these components
* are hidden when they are initially created.
*/
const modalOrPopover = swiperEl.closest('ion-modal, ion-popover');
if (modalOrPopover) {
const eventName = modalOrPopover.tagName === 'ION-MODAL' ? 'ionModalWillPresent' : 'ionPopoverWillPresent';
const overlayCallback = () => {
/**
* We need an raf here so the update
* is fired one tick after the overlay is shown.
*/
raf(() => {
swiperEl.swiper.update();
removeEventListener(modalOrPopover, eventName, overlayCallback);
});
}
addEventListener(modalOrPopover, eventName, overlayCallback);
} else {
/**
* If using slides in a page
* we need to wait for the ion-page-invisible
* class to be removed so Swiper can correctly
* compute the dimensions of the slides.
*/
const mo = new MutationObserver((m: MutationRecord[]) => {
const mutation = m[0];
const wasPageHidden = mutation.oldValue?.includes('ion-page-invisible');
const isPageHidden = ionPage.classList.contains('ion-page-invisible');

/**
* Only update Swiper if the page was
* hidden but is no longer hidden.
*/
if (!isPageHidden && isPageHidden !== wasPageHidden) {
swiperEl.swiper.update();
}
});

mo.observe(ionPage, {
attributeFilter: ['class'],
attributeOldValue: true
});
}

/**
* We also need to listen for the appload event
* which is emitted by Stencil in the
* event that Swiper is being used on the
* view that is rendered initially.
*/
const onAppLoad = () => {
swiperEl.swiper.update();
removeEventListener(window, 'appload', onAppLoad);
}

addEventListener(window, 'appload', onAppLoad);
}

export const IonicSwiper = {
name: 'ionic',
on: {
afterInit(swiper: any) {
setupSwiperInIonic(swiper);
}
}
}
37 changes: 37 additions & 0 deletions core/src/components/slides/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
The Slides component is a multi-section container. Each section can be swiped
or dragged between. It contains any number of [Slide](../slide) components.

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.

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

Licensed under MIT

## Migration

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.

### What is Swiper.js?

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.

Since then, the Swiper team has released framework specific integrations of Swiper.js for Angular, React, Vue, and more!

### What are the benefits of this change?

There are several benefits for members of the Ionic Framework community. By using the official Swiper.js framework integrations:

- 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.
- 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.
- Developers should experience fewer bugs.
- 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.
- Developers have access to new features that they previously did not have when using `ion-slides`.

### How long do I have to migrate?

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.

### How do I migrate?

Since the underlying technology that powers your slides is the same, the migration process is easy! Follow the guides below for your specific framework.

Migration for Ionic Angular users: https://ionicframework.com/docs/angular/slides
Migration for Ionic React users: https://ionicframework.com/docs/react/slides
Migration for Ionic Vue users: https://ionicframework.com/docs/vue/slides

------

The following documentation applies to the `ion-slides` component.

## Custom Animations

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.
Expand Down
4 changes: 4 additions & 0 deletions core/src/components/slides/slides.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,10 @@ export class Slides implements ComponentInterface {
*/
@Event() ionSlideTouchEnd!: EventEmitter<void>;

componentWillLoad() {
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.`);
}

connectedCallback() {
// tslint:disable-next-line: strict-type-predicates
if (typeof MutationObserver !== 'undefined') {
Expand Down
109 changes: 109 additions & 0 deletions core/src/css/ionic-swiper.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
@import "../themes/ionic.skip-warns.scss";
@import "../components/slides/slides.ios.vars.scss";

// Slides
// --------------------------------------------------

.swiper-container {

// These values are the same for iOS and MD
// We just do not add a .md or .ios class beforehand
// so the styles are easier to override by the user.
--bullet-background: #{$slides-ios-bullet-background};
--bullet-background-active: #{$slides-ios-bullet-background-active};
--progress-bar-background: #{$slides-ios-progress-bar-background};
--progress-bar-background-active: #{$slides-ios-progress-bar-background-active};
--scroll-bar-background: #{$slides-ios-scroll-bar-background};
--scroll-bar-background-active: #{$slides-ios-scroll-bar-drag-background};
/**
* @prop --bullet-background: Background of the pagination bullets
* @prop --bullet-background-active: Background of the active pagination bullet
*
* @prop --progress-bar-background: Background of the pagination progress-bar
* @prop --progress-bar-background-active: Background of the active pagination progress-bar
*
* @prop --scroll-bar-background: Background of the pagination scroll-bar
* @prop --scroll-bar-background-active: Background of the active pagination scroll-bar
*/
display: block;

user-select: none;
}

// Pagination Bullets
// --------------------------------------------------

.swiper-pagination-bullet {
background: var(--bullet-background);
}

.swiper-pagination-bullet-active {
background: var(--bullet-background-active);
}


// Pagination Progress Bar
// --------------------------------------------------

.swiper-pagination-progressbar {
background: var(--progress-bar-background);
}

.swiper-pagination-progressbar .swiper-pagination-progressbar-fill {
background: var(--progress-bar-background-active);
}

// Scrollbar
// --------------------------------------------------

.swiper-scrollbar {
background: var(--scroll-bar-background);
}

.swiper-scrollbar-drag {
background: var(--scroll-bar-background-active);
}

// Slide
// --------------------------------------------------

ion-slide {
display: block;

width: 100%;
height: 100%;
}

.slide-zoom {
display: block;

width: 100%;

text-align: center;
}

.swiper-slide {

// Center slide text vertically
display: flex;
position: relative;

flex-shrink: 0;
align-items: center;
justify-content: center;

width: 100%;
height: 100%;

font-size: 18px;

text-align: center;
box-sizing: border-box;
}

.swiper-slide img {
width: auto;
max-width: 100%;
height: auto;
max-height: 100%;
}
1 change: 1 addition & 0 deletions core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ export { IonicConfig, getMode, setupConfig } from './utils/config';
export { LIFECYCLE_WILL_ENTER, LIFECYCLE_DID_ENTER, LIFECYCLE_WILL_LEAVE, LIFECYCLE_DID_LEAVE, LIFECYCLE_WILL_UNLOAD } from './components/nav/constants';
export { menuController } from './utils/menu-controller';
export { alertController, actionSheetController, modalController, loadingController, pickerController, popoverController, toastController } from './utils/overlays';
export { IonicSwiper } from './components/slides/IonicSwiper';
1 change: 1 addition & 0 deletions packages/react/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export {
mdTransitionAnimation,
NavComponentWithProps,
setupConfig,
IonicSwiper,

SpinnerTypes,

Expand Down
3 changes: 3 additions & 0 deletions packages/vue/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ export {
// Hardware Back Button
BackButtonEvent,

// Swiper
IonicSwiper,

SpinnerTypes,

ActionSheetOptions,
Expand Down