diff --git a/demos/slider/index.html b/demos/slider/index.html
new file mode 100644
index 00000000000..155304d36d7
--- /dev/null
+++ b/demos/slider/index.html
@@ -0,0 +1,33 @@
+
+
+
+
+ Ionic Slides Basic
+
+
+
+
+
+
+
+
+ Slide 1
+
+
+
+ Slide 2
+
+
+
+ Slide 3
+
+
+
+
+
+
diff --git a/package.json b/package.json
index fc0658e11f7..07960e206ee 100644
--- a/package.json
+++ b/package.json
@@ -21,6 +21,7 @@
"@types/fs-extra": "^2.0.0",
"@types/jest": "18.1.1",
"@types/node": "7.0.5",
+ "@types/swiper": "^3.4.2",
"babel-cli": "^6.24.0",
"babel-core": "^6.24.0",
"babel-plugin-transform-define": "^1.2.0",
@@ -42,6 +43,9 @@
"parse5": "^3.0.2",
"rimraf": "2.6.1",
"rollup": "0.41.4",
+ "rollup-plugin-commonjs": "^8.0.2",
+ "rollup-plugin-node-resolve": "^3.0.0",
+ "swiper": "^3.4.2",
"tslint": "^5.1.0",
"tslint-ionic-rules": "0.0.8",
"typescript": "next"
diff --git a/scripts/build-web.ts b/scripts/build-web.ts
index dc7163520ae..c0bc045614f 100644
--- a/scripts/build-web.ts
+++ b/scripts/build-web.ts
@@ -84,6 +84,7 @@ function compileComponents() {
['ion-card', 'ion-card-content', 'ion-card-header', 'ion-card-title'],
['ion-gesture', 'ion-scroll'],
['ion-toggle']
+ ['ion-slides']
],
packages: {
fs: fs,
diff --git a/src/compiler/bundle.ts b/src/compiler/bundle.ts
index 8dd05458328..ec24321e135 100644
--- a/src/compiler/bundle.ts
+++ b/src/compiler/bundle.ts
@@ -2,7 +2,8 @@ import { bundleComponentModeStyles } from './styles';
import { Bundle, BundlerConfig, BuildContext, Component, ComponentMode, Manifest, Results } from './interfaces';
import { formatComponentRegistryProps, formatComponentModeLoader, formatModeName, formatBundleFileName, formatBundleContent, formatRegistryContent } from './formatters';
import { readFile, writeFile } from './util';
-
+import commonjs from 'rollup-plugin-commonjs';
+import nodeResolve from 'rollup-plugin-node-resolve';
export function bundle(config: BundlerConfig, ctx: BuildContext = {}): Promise {
if (!config.packages) {
@@ -77,12 +78,21 @@ function bundleComponentModule(config: BundlerConfig, component: Component) {
}
return config.packages.rollup.rollup({
- entry: entry
-
+ entry: entry,
+ plugins: [
+ nodeResolve({
+ jsnext: true,
+ main: true
+ }),
+ commonjs({
+ include: 'node_modules/**',
+ sourceMap: false
+ })
+ ]
}).then(bundle => {
const results = bundle.generate({
format: 'iife',
- moduleName: 'ionicModule'
+ moduleName: 'ionicModule',
});
let code = results.code.trim();
diff --git a/src/compiler/interfaces.ts b/src/compiler/interfaces.ts
index e263cb8a26d..ba3f79ff5f9 100644
--- a/src/compiler/interfaces.ts
+++ b/src/compiler/interfaces.ts
@@ -211,6 +211,7 @@ export interface Rollup {
footer?: string;
exports?: string;
moduleName?: string;
+ plugins?: any;
}): {
code: string;
map: any;
diff --git a/src/components/slides/slide.ios.scss b/src/components/slides/slide.ios.scss
new file mode 100644
index 00000000000..033d2f57796
--- /dev/null
+++ b/src/components/slides/slide.ios.scss
@@ -0,0 +1,2 @@
+@import "../../themes/ionic.globals";
+@import "./slide"
\ No newline at end of file
diff --git a/src/components/slides/slide.md.scss b/src/components/slides/slide.md.scss
new file mode 100644
index 00000000000..033d2f57796
--- /dev/null
+++ b/src/components/slides/slide.md.scss
@@ -0,0 +1,2 @@
+@import "../../themes/ionic.globals";
+@import "./slide"
\ No newline at end of file
diff --git a/src/components/slides/slide.scss b/src/components/slides/slide.scss
new file mode 100644
index 00000000000..312560d75e9
--- /dev/null
+++ b/src/components/slides/slide.scss
@@ -0,0 +1,34 @@
+ion-slides,
+:host {
+ width: 100%;
+ height: 100%;
+ display: block;
+}
+
+.slide-zoom {
+ display: block;
+ width: 100%;
+ text-align: center;
+}
+
+.swiper-slide {
+ width: 100%;
+ height: 100%;
+
+ box-sizing: border-box;
+
+ text-align: center;
+ font-size: 18px;
+
+ /* Center slide text vertically */
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.swiper-slide img {
+ width: auto;
+ height: auto;
+ max-width: 100%;
+ max-height: 100%;
+}
diff --git a/src/components/slides/slide.ts b/src/components/slides/slide.ts
new file mode 100644
index 00000000000..7d1e58ab5a3
--- /dev/null
+++ b/src/components/slides/slide.ts
@@ -0,0 +1,35 @@
+import { Component, h } from '../../index';
+
+ /**
+ * @name Slide
+ * @description
+ * The Slide component is a child component of [Slides](../Slides). The template
+ * should be written as `ion-slide`. Any slide content should be written
+ * in this component and it should be used in conjunction with [Slides](../Slides).
+ *
+ * See the [Slides API Docs](../Slides) for more usage information.
+ *
+ * @demo /docs/demos/src/slides/
+ * @see {@link /docs/api/components/slides/Slides/ Slides API Docs}
+ */
+@Component({
+ tag: 'ion-slide',
+ styleUrls: {
+ ios: 'slide.ios.scss',
+ md: 'slide.md.scss',
+ wp: 'slide.wp.scss'
+ },
+ shadow: false
+})
+export class Slide {
+ $el: HTMLElement;
+
+ render() {
+ return h(this, {
+ class: {
+ 'slide-zoom': true,
+ 'swiper-slide': true
+ }
+ })
+ }
+}
diff --git a/src/components/slides/slide.wp.scss b/src/components/slides/slide.wp.scss
new file mode 100644
index 00000000000..033d2f57796
--- /dev/null
+++ b/src/components/slides/slide.wp.scss
@@ -0,0 +1,2 @@
+@import "../../themes/ionic.globals";
+@import "./slide"
\ No newline at end of file
diff --git a/src/components/slides/slides.ios.scss b/src/components/slides/slides.ios.scss
new file mode 100644
index 00000000000..d178ecbe4d4
--- /dev/null
+++ b/src/components/slides/slides.ios.scss
@@ -0,0 +1,2 @@
+@import "../../themes/ionic.globals";
+@import "./slides"
\ No newline at end of file
diff --git a/src/components/slides/slides.md.scss b/src/components/slides/slides.md.scss
new file mode 100644
index 00000000000..d178ecbe4d4
--- /dev/null
+++ b/src/components/slides/slides.md.scss
@@ -0,0 +1,2 @@
+@import "../../themes/ionic.globals";
+@import "./slides"
\ No newline at end of file
diff --git a/src/components/slides/slides.scss b/src/components/slides/slides.scss
new file mode 100644
index 00000000000..8f0cdc4d604
--- /dev/null
+++ b/src/components/slides/slides.scss
@@ -0,0 +1,527 @@
+@import "../../themes/ionic.globals";
+
+/**
+ * Adopted from Swiper
+ * Most modern mobile touch slider and framework with hardware
+ * accelerated transitions.
+ *
+ * http://www.idangero.us/swiper/
+ *
+ * Copyright 2016, Vladimir Kharlampidi
+ * The iDangero.us
+ * http://www.idangero.us/
+ *
+ * Licensed under MIT
+ */
+
+.swiper-container {
+ margin-left: auto;
+ margin-right: auto;
+ position: relative;
+ overflow: hidden;
+ /* Fix of Webkit flickering */
+ z-index: 1;
+}
+
+.swiper-wrapper {
+ display: block;
+}
+
+.swiper-container-no-flexbox .swiper-slide {
+ float: left;
+}
+
+.swiper-container-vertical > .swiper-wrapper {
+ flex-direction: column;
+}
+
+.swiper-wrapper {
+ position: relative;
+ width: 100%;
+ height: 100%;
+ z-index: 1;
+ display: flex;
+ transition-property: transform;
+ box-sizing: content-box;
+}
+
+.swiper-container-android .swiper-slide,
+.swiper-wrapper {
+ transform: translate3d(0px, 0, 0);
+}
+
+.swiper-container-multirow > .swiper-wrapper {
+ flex-wrap: wrap;
+}
+
+.swiper-container-free-mode > .swiper-wrapper {
+ transition-timing-function: ease-out;
+ margin: 0 auto;
+}
+
+.swiper-slide {
+ flex-shrink: 0;
+ width: 100%;
+ height: 100%;
+ position: relative;
+}
+
+/* Auto Height */
+.swiper-container-autoheight,
+.swiper-container-autoheight .swiper-slide {
+ height: auto;
+}
+
+.swiper-container-autoheight .swiper-wrapper {
+ align-items: flex-start;
+ transition-property: transform, height;
+}
+
+/* a11y */
+.swiper-container .swiper-notification {
+ position: absolute;
+ left: 0;
+ top: 0;
+ pointer-events: none;
+ opacity: 0;
+ z-index: -1000;
+}
+
+/* IE10 Windows Phone 8 Fixes */
+.swiper-wp8-horizontal {
+ touch-action: pan-y;
+}
+
+.swiper-wp8-vertical {
+ -ms-touch-action: pan-x;
+ touch-action: pan-x;
+}
+
+/* Arrows */
+.swiper-button-prev,
+.swiper-button-next {
+ position: absolute;
+ top: 50%;
+ width: 27px;
+ height: 44px;
+ margin-top: -22px;
+ z-index: 10;
+ cursor: pointer;
+ background-size: 27px 44px;
+ background-position: center;
+ background-repeat: no-repeat;
+}
+
+.swiper-button-prev.swiper-button-disabled,
+.swiper-button-next.swiper-button-disabled {
+ opacity: 0.35;
+ cursor: auto;
+ pointer-events: none;
+}
+
+.swiper-button-prev,
+.swiper-container-rtl .swiper-button-next {
+ background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2027%2044'%3E%3Cpath%20d%3D'M0%2C22L22%2C0l2.1%2C2.1L4.2%2C22l19.9%2C19.9L22%2C44L0%2C22L0%2C22L0%2C22z'%20fill%3D'%23007aff'%2F%3E%3C%2Fsvg%3E");
+ left: 10px;
+ right: auto;
+}
+
+.swiper-button-prev.swiper-button-black,
+.swiper-container-rtl .swiper-button-next.swiper-button-black {
+ background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2027%2044'%3E%3Cpath%20d%3D'M0%2C22L22%2C0l2.1%2C2.1L4.2%2C22l19.9%2C19.9L22%2C44L0%2C22L0%2C22L0%2C22z'%20fill%3D'%23000000'%2F%3E%3C%2Fsvg%3E");
+}
+
+.swiper-button-prev.swiper-button-white,
+.swiper-container-rtl .swiper-button-next.swiper-button-white {
+ background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2027%2044'%3E%3Cpath%20d%3D'M0%2C22L22%2C0l2.1%2C2.1L4.2%2C22l19.9%2C19.9L22%2C44L0%2C22L0%2C22L0%2C22z'%20fill%3D'%23ffffff'%2F%3E%3C%2Fsvg%3E");
+}
+
+.swiper-button-next,
+.swiper-container-rtl .swiper-button-prev {
+ background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2027%2044'%3E%3Cpath%20d%3D'M27%2C22L27%2C22L5%2C44l-2.1-2.1L22.8%2C22L2.9%2C2.1L5%2C0L27%2C22L27%2C22z'%20fill%3D'%23007aff'%2F%3E%3C%2Fsvg%3E");
+ right: 10px;
+ left: auto;
+}
+
+.swiper-button-next.swiper-button-black,
+.swiper-container-rtl .swiper-button-prev.swiper-button-black {
+ background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2027%2044'%3E%3Cpath%20d%3D'M27%2C22L27%2C22L5%2C44l-2.1-2.1L22.8%2C22L2.9%2C2.1L5%2C0L27%2C22L27%2C22z'%20fill%3D'%23000000'%2F%3E%3C%2Fsvg%3E");
+}
+
+.swiper-button-next.swiper-button-white,
+.swiper-container-rtl .swiper-button-prev.swiper-button-white {
+ background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2027%2044'%3E%3Cpath%20d%3D'M27%2C22L27%2C22L5%2C44l-2.1-2.1L22.8%2C22L2.9%2C2.1L5%2C0L27%2C22L27%2C22z'%20fill%3D'%23ffffff'%2F%3E%3C%2Fsvg%3E");
+}
+
+/* Pagination Styles */
+.swiper-pagination {
+ position: absolute;
+ text-align: center;
+ transition: 300ms;
+ transform: translate3d(0, 0, 0);
+ z-index: 10;
+ pointer-events: none;
+}
+
+.swiper-pagination.swiper-pagination-hidden {
+ opacity: 0;
+}
+
+/* Common Styles */
+.swiper-pagination-fraction,
+.swiper-pagination-custom,
+.swiper-container-horizontal > .swiper-pagination-bullets {
+ bottom: 10px;
+ left: 0;
+ width: 100%;
+}
+
+/* Bullets */
+.swiper-pagination-bullet {
+ width: 8px;
+ height: 8px;
+ display: inline-block;
+ border-radius: 100%;
+ background: #000;
+ opacity: 0.2;
+ pointer-events: auto;
+}
+
+button.swiper-pagination-bullet {
+ border: none;
+ margin: 0;
+ padding: 0;
+ box-shadow: none;
+ @include appearance(none);
+}
+
+.swiper-pagination-clickable .swiper-pagination-bullet {
+ cursor: pointer;
+}
+
+.swiper-pagination-white .swiper-pagination-bullet {
+ background: #fff;
+}
+
+.swiper-pagination-bullet-active {
+ opacity: 1;
+ background: #007aff;
+}
+
+.swiper-pagination-white .swiper-pagination-bullet-active {
+ background: #fff;
+}
+
+.swiper-pagination-black .swiper-pagination-bullet-active {
+ background: #000;
+}
+
+.swiper-container-vertical > .swiper-pagination-bullets {
+ right: 10px;
+ top: 50%;
+ transform: translate3d(0px, -50%, 0);
+}
+
+.swiper-container-vertical > .swiper-pagination-bullets .swiper-pagination-bullet {
+ margin: 5px 0;
+ display: block;
+}
+
+.swiper-container-horizontal > .swiper-pagination-bullets .swiper-pagination-bullet {
+ margin: 0 5px;
+}
+
+/* Progress */
+.swiper-pagination-progress {
+ background: rgba(0, 0, 0, 0.25);
+ position: absolute;
+}
+
+.swiper-pagination-progress .swiper-pagination-progressbar {
+ background: #007aff;
+ position: absolute;
+ left: 0;
+ top: 0;
+ width: 100%;
+ height: 100%;
+ transform: scale(0);
+ transform-origin: left top;
+}
+
+.swiper-container-rtl .swiper-pagination-progress .swiper-pagination-progressbar {
+ transform-origin: right top;
+}
+
+.swiper-container-horizontal > .swiper-pagination-progress {
+ width: 100%;
+ height: 4px;
+ left: 0;
+ top: 0;
+}
+
+.swiper-container-vertical > .swiper-pagination-progress {
+ width: 4px;
+ height: 100%;
+ left: 0;
+ top: 0;
+}
+
+.swiper-pagination-progress.swiper-pagination-white {
+ background: rgba(255, 255, 255, 0.5);
+}
+
+.swiper-pagination-progress.swiper-pagination-white .swiper-pagination-progressbar {
+ background: #fff;
+}
+
+.swiper-pagination-progress.swiper-pagination-black .swiper-pagination-progressbar {
+ background: #000;
+}
+
+/* 3D Container */
+.swiper-container-3d {
+ perspective: 1200px;
+}
+
+
+.swiper-container-3d .swiper-wrapper,
+.swiper-container-3d .swiper-slide,
+.swiper-container-3d .swiper-slide-shadow-left,
+.swiper-container-3d .swiper-slide-shadow-right,
+.swiper-container-3d .swiper-slide-shadow-top,
+.swiper-container-3d .swiper-slide-shadow-bottom,
+.swiper-container-3d .swiper-cube-shadow {
+ transform-style: preserve-3d;
+}
+
+.swiper-container-3d .swiper-slide-shadow-left,
+.swiper-container-3d .swiper-slide-shadow-right,
+.swiper-container-3d .swiper-slide-shadow-top,
+.swiper-container-3d .swiper-slide-shadow-bottom {
+ position: absolute;
+ left: 0;
+ top: 0;
+ width: 100%;
+ height: 100%;
+ pointer-events: none;
+ z-index: 10;
+}
+
+.swiper-container-3d .swiper-slide-shadow-left {
+ background-image: linear-gradient(to left, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0));
+}
+
+.swiper-container-3d .swiper-slide-shadow-right {
+ background-image: linear-gradient(to right, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0));
+}
+
+.swiper-container-3d .swiper-slide-shadow-top {
+ background-image: linear-gradient(to top, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0));
+}
+
+.swiper-container-3d .swiper-slide-shadow-bottom {
+ background-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0));
+}
+
+/* Coverflow */
+.swiper-container-coverflow .swiper-wrapper,
+.swiper-container-flip .swiper-wrapper {
+ /* Windows 8 IE 10 fix */
+ perspective: 1200px;
+}
+
+/* Cube + Flip */
+.swiper-container-cube,
+.swiper-container-flip {
+ overflow: visible;
+}
+
+.swiper-container-cube .swiper-slide,
+.swiper-container-flip .swiper-slide {
+ pointer-events: none;
+ backface-visibility: hidden;
+ z-index: 1;
+}
+
+.swiper-container-cube .swiper-slide .swiper-slide,
+.swiper-container-flip .swiper-slide .swiper-slide {
+ pointer-events: none;
+}
+
+.swiper-container-cube .swiper-slide-active,
+.swiper-container-flip .swiper-slide-active,
+.swiper-container-cube .swiper-slide-active .swiper-slide-active,
+.swiper-container-flip .swiper-slide-active .swiper-slide-active {
+ pointer-events: auto;
+}
+
+.swiper-container-cube .swiper-slide-shadow-top,
+.swiper-container-flip .swiper-slide-shadow-top,
+.swiper-container-cube .swiper-slide-shadow-bottom,
+.swiper-container-flip .swiper-slide-shadow-bottom,
+.swiper-container-cube .swiper-slide-shadow-left,
+.swiper-container-flip .swiper-slide-shadow-left,
+.swiper-container-cube .swiper-slide-shadow-right,
+.swiper-container-flip .swiper-slide-shadow-right {
+ z-index: 0;
+ backface-visibility: hidden;
+}
+
+/* Cube */
+.swiper-container-cube .swiper-slide {
+ visibility: hidden;
+ transform-origin: 0 0;
+ width: 100%;
+ height: 100%;
+}
+
+.swiper-container-cube.swiper-container-rtl .swiper-slide {
+ transform-origin: 100% 0;
+}
+
+.swiper-container-cube .swiper-slide-active,
+.swiper-container-cube .swiper-slide-next,
+.swiper-container-cube .swiper-slide-prev,
+.swiper-container-cube .swiper-slide-next + .swiper-slide {
+ pointer-events: auto;
+ visibility: visible;
+}
+
+.swiper-container-cube .swiper-cube-shadow {
+ position: absolute;
+ left: 0;
+ bottom: 0px;
+ width: 100%;
+ height: 100%;
+ background: #000;
+ opacity: 0.6;
+ -webkit-filter: blur(50px);
+ filter: blur(50px);
+ z-index: 0;
+}
+
+/* Fade */
+.swiper-container-fade.swiper-container-free-mode .swiper-slide {
+ transition-timing-function: ease-out;
+}
+
+.swiper-container-fade .swiper-slide {
+ pointer-events: none;
+ transition-property: opacity;
+}
+
+.swiper-container-fade .swiper-slide .swiper-slide {
+ pointer-events: none;
+}
+
+.swiper-container-fade .swiper-slide-active,
+.swiper-container-fade .swiper-slide-active .swiper-slide-active {
+ pointer-events: auto;
+}
+
+.swiper-zoom-container {
+ width: 100%;
+ height: 100%;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ text-align: center;
+}
+
+.swiper-zoom-container > img,
+.swiper-zoom-container > svg,
+.swiper-zoom-container > canvas {
+ max-width: 100%;
+ max-height: 100%;
+ object-fit: contain;
+}
+
+/* Scrollbar */
+.swiper-scrollbar {
+ border-radius: 10px;
+ position: relative;
+ touch-action: none;
+ background: rgba(0, 0, 0, 0.1);
+}
+
+.swiper-container-horizontal > .swiper-scrollbar {
+ position: absolute;
+ left: 1%;
+ bottom: 3px;
+ z-index: 50;
+ height: 5px;
+ width: 98%;
+}
+
+.swiper-container-vertical > .swiper-scrollbar {
+ position: absolute;
+ right: 3px;
+ top: 1%;
+ z-index: 50;
+ width: 5px;
+ height: 98%;
+}
+
+.swiper-scrollbar-drag {
+ height: 100%;
+ width: 100%;
+ position: relative;
+ background: rgba(0, 0, 0, 0.5);
+ border-radius: 10px;
+ left: 0;
+ top: 0;
+}
+
+.swiper-scrollbar-cursor-drag {
+ cursor: move;
+}
+
+/* Preloader */
+.swiper-lazy-preloader {
+ width: 42px;
+ height: 42px;
+ position: absolute;
+ left: 50%;
+ top: 50%;
+ margin-left: -21px;
+ margin-top: -21px;
+ z-index: 10;
+ transform-origin: 50%;
+ animation: swiper-preloader-spin 1s steps(12, end) infinite;
+}
+
+.swiper-lazy-preloader:after {
+ display: block;
+ content: "";
+ width: 100%;
+ height: 100%;
+ background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg%20viewBox%3D'0%200%20120%20120'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20xmlns%3Axlink%3D'http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink'%3E%3Cdefs%3E%3Cline%20id%3D'l'%20x1%3D'60'%20x2%3D'60'%20y1%3D'7'%20y2%3D'27'%20stroke%3D'%236c6c6c'%20stroke-width%3D'11'%20stroke-linecap%3D'round'%2F%3E%3C%2Fdefs%3E%3Cg%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%20transform%3D'rotate(30%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%20transform%3D'rotate(60%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%20transform%3D'rotate(90%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%20transform%3D'rotate(120%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%20transform%3D'rotate(150%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.37'%20transform%3D'rotate(180%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.46'%20transform%3D'rotate(210%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.56'%20transform%3D'rotate(240%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.66'%20transform%3D'rotate(270%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.75'%20transform%3D'rotate(300%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.85'%20transform%3D'rotate(330%2060%2C60)'%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E");
+ background-position: 50%;
+ background-size: 100%;
+ background-repeat: no-repeat;
+}
+
+.swiper-lazy-preloader-white:after {
+ background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg%20viewBox%3D'0%200%20120%20120'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20xmlns%3Axlink%3D'http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink'%3E%3Cdefs%3E%3Cline%20id%3D'l'%20x1%3D'60'%20x2%3D'60'%20y1%3D'7'%20y2%3D'27'%20stroke%3D'%23fff'%20stroke-width%3D'11'%20stroke-linecap%3D'round'%2F%3E%3C%2Fdefs%3E%3Cg%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%20transform%3D'rotate(30%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%20transform%3D'rotate(60%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%20transform%3D'rotate(90%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%20transform%3D'rotate(120%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%20transform%3D'rotate(150%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.37'%20transform%3D'rotate(180%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.46'%20transform%3D'rotate(210%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.56'%20transform%3D'rotate(240%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.66'%20transform%3D'rotate(270%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.75'%20transform%3D'rotate(300%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.85'%20transform%3D'rotate(330%2060%2C60)'%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E");
+}
+
+@keyframes swiper-preloader-spin {
+ 100% {
+ transform: rotate(360deg);
+ }
+}
+
+.swiper-container {
+ width: 100%;
+ height: 100%;
+ padding: 0;
+ display: flex;
+ overflow: hidden;
+}
+
+.swiper-wrapper {
+ width: 100%;
+ height: 100%;
+ padding: 0;
+ display: flex;
+}
diff --git a/src/components/slides/slides.ts b/src/components/slides/slides.ts
new file mode 100644
index 00000000000..8e54f85cb1b
--- /dev/null
+++ b/src/components/slides/slides.ts
@@ -0,0 +1,654 @@
+import Swiper from 'swiper';
+import { Component, h, Ionic, Prop } from '../../index';
+
+/**
+ * @name Slides
+ * @description
+ * The Slides component is a multi-section container. Each section can be swiped
+ * or dragged between. It contains any number of [Slide](../Slide) components.
+ *
+ *
+ * ### Creating
+ * You should use a template to create slides and listen to slide events. The template
+ * should contain the slide container, an `` element, and any number of
+ * [Slide](../Slide) components, written as ``. Basic configuration
+ * values can be set as input properties, which are listed below. Slides events
+ * can also be listened to such as the slide changing by placing the event on the
+ * `` element. See [Usage](#usage) below for more information.
+ *
+ *
+ * ### Navigating
+ * After creating and configuring the slides, you can navigate between them
+ * by swiping or calling methods on the `Slides` instance. You can call `slideTo()` to
+ * navigate to a specific slide, or `slideNext()` to change to the slide that follows
+ * the active slide. All of the [methods](#instance-members) provided by the `Slides`
+ * instance are listed below. See [Usage](#usage) below for more information on
+ * navigating between slides.
+ *
+ *
+ * @usage
+ *
+ * You can add slides to a `@Component` using the following template:
+ *
+ * ```html
+ *
+ *
+ * Slide 1
+ *
+ *
+ * Slide 2
+ *
+ *
+ * Slide 3
+ *
+ *
+ * ```
+ *
+ * Next, we can use `ViewChild` to assign the Slides instance to
+ * your `slides` property. Now we can call any of the `Slides`
+ * [methods](#instance-members), for example we can use the Slide's
+ * `slideTo()` method in order to navigate to a specific slide on
+ * a button click. Below we call the `goToSlide()` method and it
+ * navigates to the 3rd slide:
+ *
+ * ```ts
+ * import { ViewChild } from '@angular/core';
+ * import { Slides } from 'ionic-angular';
+ *
+ * class MyPage {
+ * @ViewChild(Slides) slides: Slides;
+ *
+ * goToSlide() {
+ * this.slides.slideTo(2, 500);
+ * }
+ * }
+ * ```
+ *
+ * We can also add events to listen to on the `` element.
+ * Let's add the `ionSlideDidChange` event and call a method when the slide changes:
+ *
+ * ```html
+ *
+ * ```
+ *
+ * In our class, we add the `slideChanged()` method which gets the active
+ * index and prints it:
+ *
+ * ```ts
+ * class MyPage {
+ * ...
+ *
+ * slideChanged() {
+ * let currentIndex = this.slides.getActiveIndex();
+ * console.log("Current index is", currentIndex);
+ * }
+ * }
+ * ```
+ *
+ * @advanced
+ *
+ * There are several options available to create customized slides. Ionic exposes
+ * the most commonly used options as [inputs](http://learnangular2.com/inputs/).
+ * In order to use an option that isn't exposed as an input the following code
+ * should be used, where `freeMode` is the option to change:
+ *
+ * ```ts
+ * import { ViewChild } from '@angular/core';
+ * import { Slides } from 'ionic-angular';
+
+ * class MyPage {
+ * @ViewChild(Slides) slides: Slides;
+ *
+ * ngAfterViewInit() {
+ * this.slides.freeMode = true;
+ * }
+ * }
+ *
+ * ```
+ *
+ * To see all of the available options, take a look at the
+ * [source for slides](https://github.com/driftyco/ionic/blob/master/src/components/slides/slides.ts).
+ *
+ * @demo /docs/demos/src/slides/
+ * @see {@link /docs/components#slides Slides Component Docs}
+ *
+ * Adopted from Swiper.js:
+ * The most modern mobile touch slider and framework with
+ * hardware accelerated transitions.
+ *
+ * http://www.idangero.us/swiper/
+ *
+ * Copyright 2016, Vladimir Kharlampidi
+ * The iDangero.us
+ * http://www.idangero.us/
+ *
+ * Licensed under MIT
+ */
+
+
+@Component({
+ tag: 'ion-slides',
+ styleUrls: {
+ ios: 'slides.ios.scss',
+ md: 'slides.md.scss',
+ wp: 'slides.wp.scss'
+ },
+ shadow: false
+})
+export class Slides {
+ swiper: Swiper;
+ $el: HTMLElement;
+
+ /**
+ * @input {string} The animation effect of the slides.
+ * Possible values are: `slide`, `fade`, `cube`, `coverflow` or `flip`.
+ * Default: `slide`.
+ */
+ @Prop() effect: string = 'slide';
+
+ /**
+ * @input {number} Delay between transitions (in milliseconds). If this
+ * parameter is not passed, autoplay is disabled. Default does
+ * not have a value and does not autoplay.
+ * Default: `null`.
+ */
+ @Prop() autoplay: number;
+
+ /**
+ * @input {Slides} Pass another Slides instance or array of Slides instances
+ * that should be controlled by this Slides instance.
+ * Default: `null`.
+ */
+ @Prop() control: Slides | Slides[] = null;
+
+ /**
+ * @input {string} Swipe direction: 'horizontal' or 'vertical'.
+ * Default: `horizontal`.
+ */
+ @Prop() direction: 'horizontal' | 'vertical' = 'horizontal';
+
+ /**
+ * @input {number} Index number of initial slide. Default: `0`.
+ */
+ @Prop() initialSlide: number = 0;
+
+
+ /**
+ * @input {boolean} If true, continuously loop from the last slide to the
+ * first slide.
+ */
+ @Prop() loop: boolean = false;
+
+
+ /**
+ * @input {boolean} If true, show the pager.
+ */
+ @Prop() pager: boolean;
+
+
+ /**
+ * @input {string} Type of pagination. Possible values are:
+ * `bullets`, `fraction`, `progress`. Default: `bullets`.
+ * (Note that the pager will not show unless `pager` input
+ * is set to true).
+ */
+ @Prop() paginationType: string = 'bullets';
+
+
+ /**
+ * @input {boolean} If true, allows you to use "parallaxed" elements inside of
+ * slider.
+ */
+ @Prop() parallax: boolean = false;
+
+ /**
+ * @input {number} Slides per view. Slides visible at the same time. Default: `1`.
+ */
+ @Prop() slidesPerView: number | 'auto' = 1;
+
+ /**
+ * @input {number} Distance between slides in px. Default: `0`.
+ */
+ @Prop() spaceBetween: number = 0;
+
+ /**
+ * @input {number} Duration of transition between slides
+ * (in milliseconds). Default: `300`.
+ */
+ @Prop() speed: number = 300;
+
+
+ /**
+ * @input {boolean} If true, enables zooming functionality.
+ */
+ @Prop() zoom: boolean;
+
+ /**
+ * @input {boolean} If true, enables keyboard control
+ */
+ @Prop() keyboardControl: boolean;
+
+
+ render() {
+ return h(this,
+ h('div', {
+ class: {
+ 'swiper-container': true
+ },
+ 'data-dir': 'rtl'
+ },
+ [
+ h('div', {
+ class: {
+ 'swiper-wrapper': true
+ }
+ },
+ h('slot')
+ ),
+ h('div', {
+ class: {
+ 'swiper-pagination': true,
+ 'hide': !this.pager
+ }
+ })
+ ]
+ )
+ );
+ }
+
+ /**
+ * @hidden
+ * Height of container.
+ */
+ height: number;
+
+ /**
+ * @hidden
+ * Width of container.
+ */
+ width: number;
+
+ /**
+ * @hidden
+ * Enabled this option and swiper will be operated as usual except it will
+ * not move, real translate values on wrapper will not be set. Useful when
+ * you may need to create custom slide transition.
+ */
+ virtualTranslate = false;
+
+ /**
+ * @hidden
+ * Set to true to round values of slides width and height to prevent blurry
+ * texts on usual resolution screens (if you have such)
+ */
+ roundLengths = false;
+
+ // Slides grid
+
+ /**
+ * @hidden
+ */
+ originalEvent: any;
+
+ emitEvent(eventName: string) {
+ return (data: any) => {
+ Ionic.emit(this, eventName, data);
+ };
+ }
+
+
+ /**
+ * Private properties only useful to this class.
+ * ------------------------------------
+ */
+ private _init: boolean;
+ private _tmr: number;
+
+ /**
+ * Properties that are exposed publically but no docs.
+ * ------------------------------------
+ */
+ /** @hidden */
+ container: HTMLElement;
+ slideElements: HTMLCollection;
+ /** @hidden */
+ id: number;
+ /** @hidden */
+ renderedHeight: number;
+ /** @hidden */
+ renderedWidth: number;
+ /** @hidden */
+ slideId: string;
+ /** @hidden */
+ swipeDirection: string;
+ /** @hidden */
+ velocity: number;
+
+
+ /**
+ * Properties which are for internal use only
+ * and not exposed to the public
+ * ------------------------------------
+ */
+
+ /** @hidden */
+ nextButton: HTMLElement;
+ /** @hidden */
+ prevButton: HTMLElement;
+
+
+
+ constructor(
+ ) {
+ this.id = ++slidesId;
+ this.slideId = 'slides-' + this.id;
+ }
+
+ private _initSlides() {
+ if (!this._init) {
+ console.debug(`ion-slides, init`);
+
+ this.container = this.$el.shadowRoot.childNodes[1];
+ var slideElements = this.$el.children;
+ for (var i = 0; i < slideElements.length; i++) {
+ var item = slideElements[i];
+ item.classList.add('slide-zoom');
+ item.classList.add('swiper-slide');
+ }
+
+ var swiperOptions = {
+ slideElements: slideElements,
+ height: this.height,
+ width: this.width,
+ virtualTranslate: this.virtualTranslate,
+ roundLengths: this.roundLengths,
+ originalEvent: this.originalEvent,
+ autoplay: this.autoplay,
+ direction: this.direction,
+ initialSlide: this.initialSlide,
+ loop: this.loop,
+ pager: this.pager,
+ paginationType: this.paginationType,
+ parallax: this.parallax,
+ slidesPerView: this.slidesPerView,
+ spaceBetween: this.spaceBetween,
+ speed: this.speed,
+ zoom: this.zoom,
+ slidesPerColumn: 1,
+ slidesPerColumnFill: 'column',
+ slidesPerGroup: 1,
+ centeredSlides: false,
+ slidesOffsetBefore: 0,
+ slidesOffsetAfter: 0,
+ touchEventsTarget: 'container',
+ autoplayDisableOnInteraction: true,
+ autoplayStopOnLast: false,
+ freeMode: false,
+ freeModeMomentum: true,
+ freeModeMomentumRatio: 1,
+ freeModeMomentumBounce: true,
+ freeModeMomentumBounceRatio: 1,
+ freeModeMomentumVelocityRatio: 1,
+ freeModeSticky: false,
+ freeModeMinimumVelocity: 0.02,
+ autoHeight: false,
+ setWrapperSize: false,
+ zoomMax: 3,
+ zoomMin: 1,
+ zoomToggle: true,
+ touchRatio: 1,
+ touchAngle: 45,
+ simulateTouch: true,
+ shortSwipes: true,
+ longSwipes: true,
+ longSwipesRatio: 0.5,
+ longSwipesMs: 300,
+ followFinger: true,
+ onlyExternal: false,
+ threshold: 0,
+ touchMoveStopPropagation: true,
+ touchReleaseOnEdges: false,
+ iOSEdgeSwipeDetection: false,
+ iOSEdgeSwipeThreshold: 20,
+ paginationClickable: false,
+ paginationHide: false,
+ resistance: true,
+ resistanceRatio: 0.85,
+ watchSlidesProgress: false,
+ watchSlidesVisibility: false,
+ preventClicks: true,
+ preventClicksPropagation: true,
+ slideToClickedSlide: false,
+ loopAdditionalSlides: 0,
+ loopedSlides: null,
+ swipeHandler: null,
+ noSwiping: true,
+ runCallbacksOnInit: true,
+ controlBy: 'slide',
+ controlInverse: false,
+ keyboardControl: true,
+ coverflow: {
+ rotate: 50,
+ stretch: 0,
+ depth: 100,
+ modifier: 1,
+ slideShadows: true
+ },
+ flip: {
+ slideShadows: true,
+ limitRotation: true
+ },
+ cube: {
+ slideShadows: true,
+ shadow: true,
+ shadowOffset: 20,
+ shadowScale: 0.94
+ },
+ fade: {
+ crossFade: false
+ },
+ prevSlideMessage: 'Previous slide',
+ nextSlideMessage: 'Next slide',
+ firstSlideMessage: 'This is the first slide',
+ lastSlideMessage: 'This is the last slide',
+ onSlideChangeStart: this.emitEvent('ionSlideWillChange'),
+ onSlideChangeEnd: this.emitEvent('ionSlideDidChange'),
+ onAutoplay: this.emitEvent('ionSlideAutoplay'),
+ onAutoplayStart: this.emitEvent('ionSlideAutoplayStart'),
+ onAutoplayStop: this.emitEvent('ionSlideAutoplayStop'),
+ onSlideNextStart: this.emitEvent('ionSlideNextStarto'),
+ onSlidePrevStart: this.emitEvent('ionSlidePrevStart'),
+ onSlideNextEnd: this.emitEvent('ionSlideNextEnd'),
+ onSlidePrevEnd: this.emitEvent('ionSlidePrevEnd'),
+ onTransitionStart: this.emitEvent('ionSlideTransitionStart'),
+ onTransitionEnd: this.emitEvent('ionSlideTransitionEnd'),
+ onTap: this.emitEvent('ionSlideTap'),
+ onDoubleTap: this.emitEvent('ionSlideDoubleTap'),
+ onProgress: this.emitEvent('ionSlideProgress'),
+ onSliderMove: this.emitEvent('ionSlideDrag'),
+ onReachBeginning: this.emitEvent('ionSlideReachStart'),
+ onReachEnd: this.emitEvent('ionSlideReachEnd'),
+ onTouchStart: this.emitEvent('ionSlideTouchStart'),
+ onTouchEnd: this.emitEvent('ionSlideTouchEnd')
+ };
+
+ // init swiper core
+ this.swiper = new Swiper(this.container, swiperOptions);
+
+
+ if (this.keyboardControl) {
+ // init keyboard event listeners
+ this.enableKeyboardControl(true);
+ }
+
+ this._init = true;
+ }
+ }
+
+ /**
+ * @hidden
+ */
+ ionViewDidLoad() {
+ this._initSlides();
+ }
+
+ /**
+ * Update the underlying slider implementation. Call this if you've added or removed
+ * child slides.
+ */
+ update(debounce = 300) {
+ if (this._init) {
+ window.clearTimeout(this._tmr);
+ this._tmr = window.setTimeout(() => {
+ this.swiper.update();
+
+ // Don't allow pager to show with > 10 slides
+ if (this.length() > 10) {
+ this.paginationType = undefined;
+ }
+ }, debounce);
+ }
+ }
+
+ /**
+ * Transition to the specified slide.
+ *
+ * @param {number} index The index number of the slide.
+ * @param {number} [speed] Transition duration (in ms).
+ * @param {boolean} [runCallbacks] Whether or not to emit the `ionSlideWillChange`/`ionSlideDidChange` events. Default true.
+ */
+ slideTo(index: number, speed?: number, runCallbacks?: boolean) {
+ this.swiper.slideTo(index, speed, runCallbacks);
+ }
+
+ /**
+ * Transition to the next slide.
+ *
+ * @param {number} [speed] Transition duration (in ms).
+ * @param {boolean} [runCallbacks] Whether or not to emit the `ionSlideWillChange`/`ionSlideDidChange` events. Default true.
+ */
+ slideNext(speed?: number, runCallbacks?: boolean) {
+ this.swiper.slideNext(runCallbacks, speed);
+ }
+
+ /**
+ * Transition to the previous slide.
+ *
+ * @param {number} [speed] Transition duration (in ms).
+ * @param {boolean} [runCallbacks] Whether or not to emit the `ionSlideWillChange`/`ionSlideDidChange` events. Default true.
+ */
+ slidePrev(speed?: number, runCallbacks?: boolean) {
+ this.swiper.slidePrev(runCallbacks, speed);
+ }
+
+ /**
+ * Get the index of the active slide.
+ *
+ * @returns {number} The index number of the current slide.
+ */
+ getActiveIndex(): number {
+ return this.swiper.activeIndex;
+ }
+
+ /**
+ * Get the index of the previous slide.
+ *
+ * @returns {number} The index number of the previous slide.
+ */
+ getPreviousIndex(): number {
+ return this.swiper.previousIndex;
+ }
+
+ /**
+ * Get the total number of slides.
+ *
+ * @returns {number} The total number of slides.
+ */
+ length(): number {
+ return this.swiper.slides.length;
+ }
+
+ /**
+ * Get whether or not the current slide is the last slide.
+ *
+ * @returns {boolean} If the slide is the last slide or not.
+ */
+ isEnd(): boolean {
+ return this.isEnd();
+ }
+
+ /**
+ * Get whether or not the current slide is the first slide.
+ *
+ * @returns {boolean} If the slide is the first slide or not.
+ */
+ isBeginning(): boolean {
+ return this.isBeginning();
+ }
+
+ /**
+ * Start auto play.
+ */
+ startAutoplay() {
+ this.swiper.startAutoplay();
+ }
+
+ /**
+ * Stop auto play.
+ */
+ stopAutoplay() {
+ this.swiper.stopAutoplay();
+ }
+
+ /**
+ * Lock or unlock the ability to slide to the next slides.
+ */
+ lockSwipeToNext(shouldLockSwipeToNext: boolean) {
+ if (shouldLockSwipeToNext) {
+ return this.swiper.lockSwipeToNext();
+ }
+ this.swiper.unlockSwipeToNext();
+ }
+
+ /**
+ * Lock or unlock the ability to slide to the previous slides.
+ */
+ lockSwipeToPrev(shouldLockSwipeToPrev: boolean) {
+ if (shouldLockSwipeToPrev) {
+ return this.swiper.lockSwipeToPrev();
+ }
+ this.swiper.unlockSwipeToPrev();
+ }
+
+ /**
+ * Lock or unlock the ability to slide to change slides.
+ */
+ lockSwipes(shouldLockSwipes: boolean) {
+ if (shouldLockSwipes) {
+ return this.swiper.lockSwipes();
+ }
+ this.swiper.unlockSwipes();
+ }
+
+ /**
+ * Enable or disable keyboard control.
+ */
+ enableKeyboardControl(shouldEnableKeyboard: boolean) {
+ if (shouldEnableKeyboard) {
+ return this.swiper.enableKeyboardControl();
+ }
+ this.swiper.disableKeyboardControl();
+ }
+
+ /**
+ * @hidden
+ */
+ ngOnDestroy() {
+ this._init = false;
+
+ this.swiper.destroy(true, true);
+ this.enableKeyboardControl(false);
+ }
+}
+
+let slidesId = -1;
diff --git a/src/components/slides/slides.wp.scss b/src/components/slides/slides.wp.scss
new file mode 100644
index 00000000000..d178ecbe4d4
--- /dev/null
+++ b/src/components/slides/slides.wp.scss
@@ -0,0 +1,2 @@
+@import "../../themes/ionic.globals";
+@import "./slides"
\ No newline at end of file