diff --git a/package.json b/package.json index 3f1ffd7..548d9e7 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "description": "Angular wrapper library for Swiper", "bugs": "https://github.com/zefoy/ngx-swiper-wrapper/issues", "license": "MIT", - "version": "9.0.0", + "version": "9.0.0-0", "main": "./bundles/ngx-swiper-wrapper.umd.js", "module": "./dist/ngx-swiper-wrapper.es5.js", "typings": "./dist/ngx-swiper-wrapper.d.ts", @@ -29,8 +29,7 @@ "build:html": "cpx src/lib/swiper.component.html dist/lib/", "bundle:es5": "rollup -c config/rollup-es5.config.js --silent", "bundle:umd": "rollup -c config/rollup-umd.config.js --silent", - "minify:umd": "terser bundles/$npm_package_name.umd.js -o bundles/$npm_package_name.umd.min.js --source-map=\"filename=bundles/$npm_package_name.umd.min.js.map\"", - "prepublishOnly": "node --eval \"console.error('ERROR: Trying to publish a package that has been compiled by NGCC. This is not allowed.\\nPlease delete and rebuild the package, without compiling with NGCC, before attempting to publish.\\nNote that NGCC may have been run by importing this package into another project that is being built with Ivy enabled.\\n')\" && exit 1" + "minify:umd": "terser bundles/$npm_package_name.umd.js -o bundles/$npm_package_name.umd.min.js --source-map=\"filename=bundles/$npm_package_name.umd.min.js.map\"" }, "repository": { "type": "git", @@ -69,14 +68,5 @@ "peerDependencies": { "@angular/common": ">=5.0.0", "@angular/core": ">=5.0.0" - }, - "__processed_by_ivy_ngcc__": { - "fesm2015": "9.0.0-rc.7", - "fesm5": "9.0.0-rc.7", - "es2015": "9.0.0-rc.7", - "esm2015": "9.0.0-rc.7", - "esm5": "9.0.0-rc.7", - "main": "9.0.0-rc.7", - "module": "9.0.0-rc.7" } } diff --git a/projects/app/browserslist b/projects/app/browserslist new file mode 100644 index 0000000..8084853 --- /dev/null +++ b/projects/app/browserslist @@ -0,0 +1,12 @@ +# This file is used by the build system to adjust CSS and JS output to support the specified browsers below. +# For additional information regarding the format and rule options, please see: +# https://github.com/browserslist/browserslist#queries + +# You can see what browsers were selected by your queries by running: +# npx browserslist + +> 0.5% +last 2 versions +Firefox ESR +not dead +not IE 9-11 # For IE 9-11 support, remove 'not'. \ No newline at end of file diff --git a/projects/app/src/app/app.component.css b/projects/app/src/app/app.component.css new file mode 100644 index 0000000..552afe9 --- /dev/null +++ b/projects/app/src/app/app.component.css @@ -0,0 +1,91 @@ +.app-title { + font-size: 24px; +} + +.box-title { + font-size: 20px; +} + +.box-container { + box-sizing: border-box; + width: 800px; + height: 500px; + max-width: 90%; + max-height: 90%; + padding: 24px; + margin: 24px auto; + border-radius: 4px; + + color: #555; + background-color: #eee; + box-shadow: + 0 6px 20px rgba(0, 0, 0, 0.18), + 0 6px 6px rgba(0, 0, 0, 0.25); +} + +.info-container { + padding: 12px 16px; + + line-height: 24px; +} + +.action-container, +.content-container { + position: relative; + + overflow: auto; + height: 300px; + min-height: 0; + margin: 8px 16px; + border-radius: 4px; + + background-color: #fff; +} + +.action-container { + padding: 16px; +} + +.vertical-container, +.horizontal-container { + min-height: 0 !important; +} + +.action-button { + box-sizing: border-box; + width: calc(100% - 16px); + min-height: 35px; + padding: 4px 16px; + margin: 8px; + border: 1px solid #555; + border-radius: 4px; + + cursor: pointer; + font-size: 14px; + font-weight: bold; + line-height: 14px; + text-align: center; +} + +.action-button:hover { + color: #fff; + background-color: #555; +} + +.swiper-container { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; +} + +.swiper-slide { + color: #aaa; + background-color: #eee; +} + +.swiper-slide-active { + color: #fff; + background-color: #aaa; +} diff --git a/projects/app/src/app/app.component.html b/projects/app/src/app/app.component.html new file mode 100644 index 0000000..ac02b1f --- /dev/null +++ b/projects/app/src/app/app.component.html @@ -0,0 +1,57 @@ +
+
Simple example app for the ngx-swiper-wrapper
+ +
+ You can change the basic settings of the Swiper area by clicking the buttons on the controls pane. +
+ +
+
+
Controls
+ +
+
{{(type == "component") ? "Switch to directive" : "Switch to component"}}
+ +
{{(config.direction == "horizontal") ? "Change to vertical" : "Change to horizontal"}}
+ +
Toggle slides per view
+ +
Toggle overlay controls
+ +
{{config.keyboard ? "Disable arrow keys" : "Enable arrow keys"}}
+ +
{{config.mousewheel ? "Disable mouse wheel" : "Enable mouse wheel"}}
+
+
+ +
+
Swiper
+ +
+ +
+
+ {{slide}} +
+
+
+ +
+
+
+
+ {{slide}} +
+
+
+ +
+
+ +
+
+
+
+
+
+
diff --git a/projects/app/src/app/app.component.ts b/projects/app/src/app/app.component.ts new file mode 100644 index 0000000..3194e38 --- /dev/null +++ b/projects/app/src/app/app.component.ts @@ -0,0 +1,116 @@ +import { Component, ViewChild } from '@angular/core'; + +import { SwiperComponent, SwiperDirective, SwiperConfigInterface, + SwiperScrollbarInterface, SwiperPaginationInterface } from 'ngx-swiper-wrapper'; + +@Component({ + selector: 'my-app', + moduleId: 'src/app/app.component', + templateUrl: 'app.component.html', + styleUrls: [ 'app.component.css' ] +}) +export class AppComponent { + public show: boolean = true; + + public slides = [ + 'First slide', + 'Second slide', + 'Third slide', + 'Fourth slide', + 'Fifth slide', + 'Sixth slide' + ]; + + public type: string = 'component'; + + public disabled: boolean = false; + + public config: SwiperConfigInterface = { + a11y: true, + direction: 'horizontal', + slidesPerView: 1, + keyboard: true, + mousewheel: true, + scrollbar: false, + navigation: true, + pagination: false + }; + + private scrollbar: SwiperScrollbarInterface = { + el: '.swiper-scrollbar', + hide: false, + draggable: true + }; + + private pagination: SwiperPaginationInterface = { + el: '.swiper-pagination', + clickable: true, + hideOnClick: false + }; + + @ViewChild(SwiperComponent, { static: false }) componentRef?: SwiperComponent; + @ViewChild(SwiperDirective, { static: false }) directiveRef?: SwiperDirective; + + constructor() {} + + public toggleType(): void { + this.type = (this.type === 'component') ? 'directive' : 'component'; + } + + public toggleDisabled(): void { + this.disabled = !this.disabled; + } + + public toggleDirection(): void { + this.config.direction = (this.config.direction === 'horizontal') ? 'vertical' : 'horizontal'; + } + + public toggleSlidesPerView(): void { + if (this.config.slidesPerView !== 1) { + this.config.slidesPerView = 1; + } else { + this.config.slidesPerView = 2; + } + } + + public toggleOverlayControls(): void { + if (this.config.navigation) { + this.config.scrollbar = false; + this.config.navigation = false; + + this.config.pagination = this.pagination; + } else if (this.config.pagination) { + this.config.navigation = false; + this.config.pagination = false; + + this.config.scrollbar = this.scrollbar; + } else { + this.config.scrollbar = false; + this.config.pagination = false; + + this.config.navigation = true; + } + + if (this.type === 'directive' && this.directiveRef) { + this.directiveRef.setIndex(0); + } else if (this.type === 'component' && this.componentRef && this.componentRef.directiveRef) { + this.componentRef.directiveRef.setIndex(0); + } + } + + public toggleKeyboardControl(): void { + this.config.keyboard = !this.config.keyboard; + } + + public toggleMouseWheelControl(): void { + this.config.mousewheel = !this.config.mousewheel; + } + + public onIndexChange(index: number): void { + console.log('Swiper index: ', index); + } + + public onSwiperEvent(event: string): void { + console.log('Swiper event: ', event); + } +} diff --git a/projects/app/src/app/app.module.ts b/projects/app/src/app/app.module.ts new file mode 100644 index 0000000..f1f9d08 --- /dev/null +++ b/projects/app/src/app/app.module.ts @@ -0,0 +1,42 @@ +import { NgModule } from '@angular/core'; + +import { BrowserModule } from '@angular/platform-browser'; + +import { FlexLayoutModule } from '@angular/flex-layout'; + +import { SwiperModule, SwiperConfigInterface, + SWIPER_CONFIG } from 'ngx-swiper-wrapper'; + +import { AppComponent } from './app.component'; + +const DEFAULT_SWIPER_CONFIG: SwiperConfigInterface = { + observer: true, + direction: 'horizontal', + threshold: 50, + spaceBetween: 5, + slidesPerView: 1, + centeredSlides: true +}; + +@NgModule({ + bootstrap: [ + AppComponent + ], + declarations: [ + AppComponent + ], + imports: [ + SwiperModule, + BrowserModule, + FlexLayoutModule + ], + exports: [ + ], + providers: [ + { + provide: SWIPER_CONFIG, + useValue: DEFAULT_SWIPER_CONFIG + } + ] +}) +export class AppModule {} diff --git a/projects/app/src/environments/environment.prod.ts b/projects/app/src/environments/environment.prod.ts new file mode 100644 index 0000000..3612073 --- /dev/null +++ b/projects/app/src/environments/environment.prod.ts @@ -0,0 +1,3 @@ +export const environment = { + production: true +}; diff --git a/projects/app/src/environments/environment.ts b/projects/app/src/environments/environment.ts new file mode 100644 index 0000000..7b4f817 --- /dev/null +++ b/projects/app/src/environments/environment.ts @@ -0,0 +1,16 @@ +// This file can be replaced during build by using the `fileReplacements` array. +// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. +// The list of file replacements can be found in `angular.json`. + +export const environment = { + production: false +}; + +/* + * For easier debugging in development mode, you can import the following file + * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. + * + * This import should be commented out in production mode because it will have a negative impact + * on performance if an error is thrown. + */ +// import 'zone.js/dist/zone-error'; // Included with Angular CLI. diff --git a/projects/app/src/favicon.ico b/projects/app/src/favicon.ico new file mode 100644 index 0000000..997406a Binary files /dev/null and b/projects/app/src/favicon.ico differ diff --git a/projects/app/src/index.html b/projects/app/src/index.html new file mode 100644 index 0000000..57715c8 --- /dev/null +++ b/projects/app/src/index.html @@ -0,0 +1,72 @@ + + + + + + + Example app + + + + + + + + + + + +
+ + diff --git a/projects/app/src/main.ts b/projects/app/src/main.ts new file mode 100644 index 0000000..311c44b --- /dev/null +++ b/projects/app/src/main.ts @@ -0,0 +1,5 @@ +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; + +import { AppModule } from './app/app.module'; + +platformBrowserDynamic().bootstrapModule(AppModule); diff --git a/projects/app/src/polyfills.ts b/projects/app/src/polyfills.ts new file mode 100644 index 0000000..741c886 --- /dev/null +++ b/projects/app/src/polyfills.ts @@ -0,0 +1 @@ +import 'zone.js/dist/zone'; diff --git a/projects/app/src/styles.css b/projects/app/src/styles.css new file mode 100644 index 0000000..90d4ee0 --- /dev/null +++ b/projects/app/src/styles.css @@ -0,0 +1 @@ +/* You can add global styles to this file, and also import other style files */ diff --git a/projects/app/stylelint.json b/projects/app/stylelint.json new file mode 100644 index 0000000..f76cdce --- /dev/null +++ b/projects/app/stylelint.json @@ -0,0 +1,230 @@ +{ + "plugins": [ + "stylelint-order" + ], + "extends": [ + "stylelint-config-standard" + ], + "rules": { + "color-hex-case": "lower", + "color-no-invalid-hex": true, + + "font-family-no-missing-generic-family-keyword": null, + "function-calc-no-unspaced-operator": true, + "function-comma-space-after": "always-single-line", + "function-comma-space-before": "never", + "function-name-case": "lower", + "function-url-quotes": "always", + "function-whitespace-after": "always", + + "number-leading-zero": "always", + "number-no-trailing-zeros": true, + "length-zero-no-unit": true, + + "string-no-newline": true, + "string-quotes": "single", + + "unit-case": "lower", + "unit-no-unknown": true, + "unit-whitelist": [ + "px", + "%", + "deg", + "ms", + "em" + ], + + "value-list-comma-space-after": "always-single-line", + "value-list-comma-space-before": "never", + + "shorthand-property-no-redundant-values": true, + + "property-case": "lower", + + "at-rule-empty-line-before": [ + "always", + { + "ignore": [ + "blockless-after-blockless" + ] + } + ], + + "at-rule-no-unknown": null, + + "declaration-block-no-duplicate-properties": true, + "declaration-block-trailing-semicolon": "always", + "declaration-block-single-line-max-declarations": 1, + "declaration-block-semicolon-space-before": "never", + "declaration-block-semicolon-space-after": "always-single-line", + "declaration-block-semicolon-newline-before": "never-multi-line", + "declaration-block-semicolon-newline-after": "always-multi-line", + + "block-closing-brace-newline-after": "always", + "block-closing-brace-newline-before": "always-multi-line", + "block-no-empty": true, + "block-opening-brace-newline-after": "always-multi-line", + "block-opening-brace-space-before": "always-multi-line", + + "selector-attribute-brackets-space-inside": "never", + "selector-attribute-operator-space-after": "never", + "selector-attribute-operator-space-before": "never", + "selector-combinator-space-after": "always", + "selector-combinator-space-before": "always", + "selector-pseudo-class-case": "lower", + "selector-pseudo-class-parentheses-space-inside": "never", + "selector-pseudo-element-case": "lower", + "selector-pseudo-element-colon-notation": "double", + "selector-pseudo-element-no-unknown": true, + "selector-type-no-unknown": null, + "selector-type-case": "lower", + "selector-max-id": 0, + + "declaration-empty-line-before": null, + + "no-descending-specificity": null, + + "order/properties-order": [ + [ + { + "properties": [ + "content", + "direction" + ] + }, { + "emptyLineBefore": "always", + "properties": [ + "float", + "position", + "z-index", + "top", + "right", + "bottom", + "left" + ] + }, { + "emptyLineBefore": "always", + "properties": [ + "visibility", + "opacity", + "display", + "overflow", + "box-sizing", + "flex", + "flex-basis", + "flex-direction", + "flex-flow", + "flex-grow", + "flex-shrink", + "flex-wrap", + "align-self", + "align-items", + "align-content", + "justify-content", + "order", + "width", + "height", + "min-width", + "min-height", + "max-width", + "max-height", + "padding", + "padding-top", + "padding-right", + "padding-bottom", + "padding-left", + "margin", + "margin-top", + "margin-right", + "margin-bottom", + "margin-left", + "border", + "border-top", + "border-right", + "border-bottom", + "border-left", + "border-width", + "border-top-width", + "border-right-width", + "border-bottom-width", + "border-left-width", + "border-style", + "border-top-style", + "border-right-style", + "border-bottom-style", + "border-left-style", + "border-color", + "border-top-color", + "border-right-color", + "border-bottom-color", + "border-left-color", + "border-radius", + "border-top-left-radius", + "border-top-right-radius", + "border-bottom-right-radius", + "border-bottom-left-radius", + "outline" + ] + }, { + "emptyLineBefore": "always", + "properties": [ + "cursor", + "resize", + "user-select", + "touch-action", + "pointer-events", + "font-size", + "font-style", + "font-weight", + "font-family", + "line-height", + "white-space", + "text-align", + "text-shadow", + "text-decoration", + "text-transform", + "text-overflow", + "letter-spacing", + "list-style-type", + "object-fit", + "vertical-align", + "color", + "background", + "background-size", + "background-color", + "background-image", + "background-repeat", + "background-position", + "background-attachment", + "box-shadow" + ] + }, { + "emptyLineBefore": "always", + "properties": [ + "filter", + "animation", + "animation-name", + "animation-delay", + "animation-duration", + "animation-direction", + "animation-fill-mode", + "animation-play-state", + "animation-iteration-count", + "animation-timing-function", + "transform", + "transform-style", + "transform-origin", + "transition", + "transition-delay", + "transition-duration", + "transition-property", + "transition-timing-function" + ] + } + ], + { + "unspecified": "bottomAlphabetical" + } + ] + } +} diff --git a/projects/app/tsconfig.json b/projects/app/tsconfig.json new file mode 100644 index 0000000..809c09c --- /dev/null +++ b/projects/app/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "../../out-tsc/app", + "types": [] + }, + "files": [ + "src/main.ts", + "src/polyfills.ts" + ], + "include": [ + "src/**/*.d.ts" + ] +} diff --git a/projects/app/tslint.json b/projects/app/tslint.json new file mode 100644 index 0000000..0946f20 --- /dev/null +++ b/projects/app/tslint.json @@ -0,0 +1,3 @@ +{ + "extends": "../../tslint.json" +} diff --git a/projects/lib/README.md b/projects/lib/README.md new file mode 100644 index 0000000..56551a6 --- /dev/null +++ b/projects/lib/README.md @@ -0,0 +1,174 @@ +# Angular Swiper Wrapper + +npm version + +This is an Angular wrapper library for the [Swiper](http://idangero.us/swiper/). To use this library you should get familiar with the Swiper documentation as well since this documentation only explains details specific to this wrapper. + +This documentation is for the latest 6.x.x version which requires Angular 5 or newer. For Angular 4 you need to use the latest 4.x.x version. Documentation for the 4.x.x can be found from here. + +### Quick links + +[Example application](https://zefoy.github.io/ngx-swiper-wrapper/) + | +[StackBlitz example](https://stackblitz.com/github/zefoy/ngx-swiper-wrapper/tree/master) + | +[Swiper documentation](http://idangero.us/swiper/api/) + +### Building the library + +```bash +npm install +npm run build +``` + +### Running the example + +```bash +npm install +npm run start +``` + +### Installing and usage + +```bash +npm install ngx-swiper-wrapper --save +``` + +##### Load the module for your app (with global configuration): + +Providing the global configuration is optional and when used you should only provide the configuration in your root module. + +```javascript +import { SwiperModule } from 'ngx-swiper-wrapper'; +import { SWIPER_CONFIG } from 'ngx-swiper-wrapper'; +import { SwiperConfigInterface } from 'ngx-swiper-wrapper'; + +const DEFAULT_SWIPER_CONFIG: SwiperConfigInterface = { + direction: 'horizontal', + slidesPerView: 'auto' +}; + +@NgModule({ + ... + imports: [ + ... + SwiperModule + ], + providers: [ + { + provide: SWIPER_CONFIG, + useValue: DEFAULT_SWIPER_CONFIG + } + ] +}) +``` + +##### Use it in your HTML template (with custom configuration): + +This library provides two ways to create a Swiper element, component for simple use cases and directive for more custom use cases. + +**COMPONENT USAGE** + +Simply replace the element that would ordinarily be passed to `Swiper` with the swiper component. + +**NOTE:** Component provides default elements for the scrollbar, navigation and pagination which you can enable by setting the appropriate configuration to 'true' or by using the default selector. If you want to use custom elements then you might want to use the directive instead. + +```html + +
+ Swiper slide content +
+
+``` + +```javascript +[config] // Custom config to override the global defaults. + +[index] // Can be used to set the active slide index. +[disabled] // Disables changing of slides (locks the Swiper). + +[useSwiperClass] // Use 'swiper' class (use provided default styles). + +(indexChange) // Event handler for the Swiper index change event. + +() // All Swiper events / callbacks work as bindings. + // Conflicting events are prefixed with 'swiper': + // click, tap, doubleTap, touch*, transition* + // Example: touchStart -> swiperTouchStart +``` + +**DIRECTIVE USAGE** + +When using only the directive you need to provide your own theming or import the default theme: + +```css +@import '~swiper/dist/css/swiper.min.css'; +``` + +Swiper directive can be used in correctly structured div element with optional custom configuration: + +```html +
+
+
+ Swiper slide content +
+
+ +
+
+ +
+
+
+``` + +```javascript +[swiper] // Can be used to provide optional custom config. + +[index] // Can be used to set the active slide index. +[disabled] // Disables changing of slides (locks the Swiper). +[performance] // Emit all Swiper events outside the Angular zone. + +(indexChange) // Event handler for the Swiper index change event. + +() // All Swiper events / callbacks work as bindings. + // Conflicting events are prefixed with 'swiper': + // click, tap, doubleTap, touch*, transition* + // Example: touchStart -> swiperTouchStart +``` + +##### Available configuration options (custom / global configuration): + +This library supports all Swiper configuration options and few extra options for easier usage. + +```javascript +observer // Set to to true to enable automatic update calls. +direction // Direction of the Swiper (Default: 'horizontal'). +threshold // Distance needed for the swipe action (Default: 0). +spaceBetween // Space in pixels between the Swiper items (Default: 0). +slidesPerView // Number of the items per view or 'auto' (Default: 1). +centeredSlides // Align active item on center not left (Default: false). +``` + +For more detailed documentation with all the supported events / options see the Swiper documentation. + +##### Available control / helper functions (provided by the directive): + +```javascript +swiper() // Returns reference to the Swiper instance. + +init() // Starts Swiper (when init set to false). +update() // Updates Swiper elements / classes / etc. + +getIndex(real?) // Returns the active or real slide index. +setIndex(index, speed?, silent?) // Runs transition to slide with given index. + +nextSlide(speed?, silent?) // Runs transition to the next slide index. +prevSlide(speed?, silent?) // Runs transition to the previous slide index. + +stopAutoplay(reset?) // Stops and optionally resets the autoplay. +startAutoplay(reset?) // Starts and optionally resets the autoplay. +``` + +Above functions can be accessed through the directive reference (available as directiveRef in the component). diff --git a/projects/lib/ng-package.json b/projects/lib/ng-package.json new file mode 100644 index 0000000..4eb2631 --- /dev/null +++ b/projects/lib/ng-package.json @@ -0,0 +1,10 @@ +{ + "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", + "dest": "../../dist/lib", + "lib": { + "entryFile": "src/public-api.ts", + "umdModuleIds": { + "swiper": "Swiper" + } + } +} diff --git a/projects/lib/package.json b/projects/lib/package.json new file mode 100644 index 0000000..baa9ca4 --- /dev/null +++ b/projects/lib/package.json @@ -0,0 +1,12 @@ +{ + "name": "ngx-swiper-wrapper", + "description": "Angular wrapper library for Swiper", + "bugs": "https://github.com/zefoy/ngx-swiper-wrapper/issues", + "version": "9.0.0-1", + "license": "MIT", + "peerDependencies": { + "@angular/common": "^9.0.0", + "@angular/core": "^9.0.0", + "tslib": "^1.10.0" + } +} diff --git a/projects/lib/src/lib/swiper.component.css b/projects/lib/src/lib/swiper.component.css new file mode 100644 index 0000000..00d07f3 --- /dev/null +++ b/projects/lib/src/lib/swiper.component.css @@ -0,0 +1,120 @@ +swiper[fxflex] { + display: flex; + flex-direction: inherit; + min-width: 0; + min-height: 0; + + -webkit-box-direction: inherit; + -webkit-box-orient: inherit; +} + +swiper[fxflex] > .swiper.s-wrapper { + -ms-flex: 1 1 auto; + + flex: 1 1 auto; + min-width: 0; + min-height: 0; + + -webkit-box-flex: 1; +} + +swiper > .swiper.s-wrapper { + width: 100%; + height: 100%; +} + +swiper > .swiper.s-wrapper .swiper-wrapper .swiper-slide { + will-change: transform; + overflow: auto; + width: 100%; + height: 100%; + max-width: 100%; + max-height: 100%; +} + +swiper > .swiper.s-wrapper .swiper-pagination { + pointer-events: none; +} + +swiper > .swiper.s-wrapper .swiper-pagination .swiper-pagination-handle { + position: relative; + + display: inline-block; + padding: 4px; + margin: 2px; + + cursor: pointer; + pointer-events: all; +} + +swiper > .swiper.s-wrapper .swiper-pagination .swiper-pagination-handle .swiper-pagination-bullet { + display: inline-block; + + margin: 0; + + pointer-events: none; +} + +swiper > .swiper.s-wrapper .swiper-pagination .swiper-pagination-handle .swiper-pagination-bullet.swiper-pagination-bullet-last, +swiper > .swiper.s-wrapper .swiper-pagination .swiper-pagination-handle .swiper-pagination-bullet.swiper-pagination-bullet-first { + border: 1px solid rgba(0, 0, 0, 0.5); +} + +swiper > .swiper.s-wrapper.swiper-container-vertical > .swiper-button-prev { + top: 10px; + left: 50%; + + margin-top: 0; + margin-left: -13px; + + transform: rotate(90deg); +} + +swiper > .swiper.s-wrapper.swiper-container-vertical > .swiper-button-next { + top: auto; + bottom: 10px; + left: 50%; + + margin-top: 0; + margin-left: -13px; + + transform: rotate(90deg); +} + +swiper > .swiper.s-wrapper.swiper-container-vertical > .swiper-scrollbar { + width: 8px; + + transition: width 250ms ease-in-out; +} + +swiper > .swiper.s-wrapper.swiper-container-vertical > .swiper-scrollbar:hover { + width: 16px; +} + +swiper > .swiper.s-wrapper.swiper-container-vertical > .swiper-pagination .swiper-pagination-handle { + display: block; +} + +swiper > .swiper.s-wrapper.swiper-container-vertical > .swiper-pagination .swiper-pagination-handle .swiper-pagination-bullet { + display: inline-block; +} + +swiper > .swiper.s-wrapper.swiper-container-vertical > .swiper-pagination .swiper-pagination-handle .swiper-pagination-bullet.swiper-pagination-bullet-last, +swiper > .swiper.s-wrapper.swiper-container-vertical > .swiper-pagination .swiper-pagination-handle .swiper-pagination-bullet.swiper-pagination-bullet-first { + margin: 0 -1px; +} + +swiper > .swiper.s-wrapper.swiper-container-horizontal > .swiper-scrollbar { + height: 8px; + + transition: height 250ms ease-in-out; +} + +swiper > .swiper.s-wrapper.swiper-container-horizontal > .swiper-scrollbar:hover { + height: 16px; +} + +swiper > .swiper.s-wrapper.swiper-container-horizontal > .swiper-pagination .swiper-pagination-handle .swiper-pagination-bullet.swiper-pagination-bullet-last, +swiper > .swiper.s-wrapper.swiper-container-horizontal > .swiper-pagination .swiper-pagination-handle .swiper-pagination-bullet.swiper-pagination-bullet-first { + margin: -1px 0; +} diff --git a/projects/lib/src/lib/swiper.component.html b/projects/lib/src/lib/swiper.component.html new file mode 100644 index 0000000..793377e --- /dev/null +++ b/projects/lib/src/lib/swiper.component.html @@ -0,0 +1,12 @@ +
+
+ +
+ +
+ +
+
+ +
+
diff --git a/projects/lib/src/lib/swiper.component.ts b/projects/lib/src/lib/swiper.component.ts new file mode 100644 index 0000000..a418414 --- /dev/null +++ b/projects/lib/src/lib/swiper.component.ts @@ -0,0 +1,243 @@ +import { PLATFORM_ID } from '@angular/core'; +import { isPlatformBrowser } from '@angular/common'; +import { NgZone, Inject, Optional, ElementRef, Component, + AfterViewInit, OnDestroy, Input, Output, EventEmitter, + ViewChild, ViewEncapsulation, ChangeDetectorRef } from '@angular/core'; + +import { SwiperDirective } from './swiper.directive'; + +import { SWIPER_CONFIG, SwiperConfig, SwiperConfigInterface, + SwiperEvent, SwiperEvents } from './swiper.interfaces'; + +@Component({ + selector: 'swiper', + exportAs: 'ngxSwiper', + templateUrl: './swiper.component.html', + styleUrls: [ + './swiper.component.css', + '../../../../node_modules/swiper/css/swiper.min.css' + ], + encapsulation: ViewEncapsulation.None +}) +export class SwiperComponent implements AfterViewInit, OnDestroy { + private mo: MutationObserver | null = null; + + public swiperConfig: any = null; + public paginationBackup: any = null; + public paginationConfig: any = null; + + @Input() index: number | null = null; + + @Input() disabled: boolean = false; + + @Input() performance: boolean = false; + + @Input() config?: SwiperConfigInterface; + + @Input() useSwiperClass: boolean = true; + + @Output() indexChange = new EventEmitter(); + + @ViewChild('swiperSlides', { static: false }) swiperSlides?: ElementRef; + + @ViewChild(SwiperDirective, { static: false }) directiveRef?: SwiperDirective; + + get isAtLast(): boolean { + return (!this.directiveRef || !this.directiveRef.swiper()) ? + false : this.directiveRef.swiper()['isEnd']; + } + + get isAtFirst(): boolean { + return (!this.directiveRef || !this.directiveRef.swiper()) ? + false : this.directiveRef.swiper()['isBeginning']; + } + + @Output('init' ) S_INIT = new EventEmitter(); + @Output('beforeDestroy' ) S_BEFOREDESTROY = new EventEmitter(); + + @Output('scroll' ) S_SCROLL = new EventEmitter(); + @Output('progress' ) S_PROGRESS = new EventEmitter(); + @Output('keyPress' ) S_KEYPRESS = new EventEmitter(); + + @Output('resize' ) S_RESIZE = new EventEmitter(); + @Output('breakpoint' ) S_BREAKPOINT = new EventEmitter(); + @Output('zoomChange' ) S_ZOOMCHANGE = new EventEmitter(); + @Output('afterResize' ) S_AFTERRESIZE = new EventEmitter(); + @Output('beforeResize' ) S_BEFORERESIZE = new EventEmitter(); + + @Output('sliderMove' ) S_SLIDERMOVE = new EventEmitter(); + @Output('slideChange' ) S_SLIDECHANGE = new EventEmitter(); + + @Output('setTranslate' ) S_SETTRANSLATE = new EventEmitter(); + @Output('setTransition' ) S_SETTRANSITION = new EventEmitter(); + + @Output('fromEdge' ) S_FROMEDGE = new EventEmitter(); + @Output('reachEnd' ) S_REACHEND = new EventEmitter(); + @Output('reachBeginning' ) S_REACHBEGINNING = new EventEmitter(); + + @Output('autoplay' ) S_AUTOPLAY = new EventEmitter(); + @Output('autoplayStart' ) S_AUTOPLAYSTART = new EventEmitter(); + @Output('autoplayStop' ) S_AUTOPLAYSTOP = new EventEmitter(); + + @Output('imagesReady' ) S_IMAGESREADY = new EventEmitter(); + @Output('lazyImageLoad' ) S_LAZYIMAGELOAD = new EventEmitter(); + @Output('lazyImageReady' ) S_LAZYIMAGEREADY = new EventEmitter(); + + @Output('scrollDragEnd' ) S_SCROLLDRAGEND = new EventEmitter(); + @Output('scrollDragMove' ) S_SCROLLDRAGMOVE = new EventEmitter(); + @Output('scrollDragStart' ) S_SCROLLDRAGSTART = new EventEmitter(); + + @Output('navigationHide' ) S_NAVIGATIONHIDE = new EventEmitter(); + @Output('navigationShow' ) S_NAVIGATIONSHOW = new EventEmitter(); + + @Output('paginationRender' ) S_PAGINATIONRENDER = new EventEmitter(); + @Output('paginationUpdate' ) S_PAGINATIONUPDATE = new EventEmitter(); + @Output('paginationHide' ) S_PAGINATIONHIDE = new EventEmitter(); + @Output('paginationShow' ) S_PAGINATIONSHOW = new EventEmitter(); + + @Output('swiperTap' ) S_TAP = new EventEmitter(); + @Output('swiperClick' ) S_CLICK = new EventEmitter(); + @Output('swiperDoubleTap' ) S_DOUBLETAP = new EventEmitter(); + @Output('swiperTouchEnd' ) S_TOUCHEND = new EventEmitter(); + @Output('swiperTouchMove' ) S_TOUCHMOVE = new EventEmitter(); + @Output('swiperTouchStart' ) S_TOUCHSTART = new EventEmitter(); + @Output('swiperTouchMoveOpposite' ) S_TOUCHMOVEOPPOSITE = new EventEmitter(); + @Output('swiperTransitionEnd' ) S_TRANSITIONEND = new EventEmitter(); + @Output('swiperTransitionStart' ) S_TRANSITIONSTART = new EventEmitter(); + + @Output('slidePrevTransitionEnd' ) S_SLIDEPREVTRANSITIONEND = new EventEmitter(); + @Output('slidePrevTransitionStart' ) S_SLIDEPREVTRANSITIONSTART = new EventEmitter(); + @Output('slideNextTransitionEnd' ) S_SLIDENEXTTRANSITIONEND = new EventEmitter(); + @Output('slideNextTransitionStart' ) S_SLIDENEXTTRANSITIONSTART = new EventEmitter(); + @Output('slideChangeTransitionEnd' ) S_SLIDECHANGETRANSITIONEND = new EventEmitter(); + @Output('slideChangeTransitionStart' ) S_SLIDECHANGETRANSITIONSTART = new EventEmitter(); + + constructor(private zone: NgZone, private cdRef: ChangeDetectorRef, + @Inject(PLATFORM_ID) private platformId: Object, + @Optional() @Inject(SWIPER_CONFIG) private defaults: SwiperConfigInterface) {} + + ngAfterViewInit(): void { + if (!isPlatformBrowser(this.platformId)) { + return; + } + + this.zone.runOutsideAngular(() => { + this.updateClasses(); + + if (this.swiperSlides && typeof MutationObserver !== 'undefined') { + this.mo = new MutationObserver(() => { + this.updateClasses(); + }); + + this.mo.observe(this.swiperSlides.nativeElement, { childList: true }); + } + }); + + window.setTimeout(() => { + if (this.directiveRef) { + this.S_INIT.emit(); + + this.directiveRef.indexChange = this.indexChange; + + SwiperEvents.forEach((eventName: SwiperEvent) => { + if (this.directiveRef) { + const output = `S_${eventName.replace('swiper', '').toUpperCase()}`; + + const directiveOutput = output as keyof SwiperDirective; + const componentOutput = output as keyof SwiperComponent; + + this.directiveRef[directiveOutput] = this[componentOutput] as never; + } + }); + } + }, 0); + } + + ngOnDestroy(): void { + if (this.mo) { + this.mo.disconnect(); + } + + if (this.config && this.paginationBackup) { + this.config.pagination = this.paginationBackup; + } + } + + public getConfig(): SwiperConfigInterface { + this.swiperConfig = new SwiperConfig(this.defaults); + + this.swiperConfig.assign(this.config); // Custom configuration + + if (this.swiperSlides && (this.swiperConfig.pagination === true || + (this.swiperConfig.pagination && typeof this.swiperConfig.pagination === 'object' && + (!this.swiperConfig.pagination.type || this.swiperConfig.pagination.type === 'bullets') && + !this.swiperConfig.pagination.renderBullet && this.swiperConfig.pagination.el === '.swiper-pagination'))) + { + this.config = this.config || {}; + + if (!this.paginationConfig) { + this.paginationBackup = this.config.pagination; + + this.paginationConfig = { + el: '.swiper-pagination', + + renderBullet: (index: number, className: string) => { + let children = this.swiperSlides ? Array.from(this.swiperSlides.nativeElement.children) : []; + + children = children.filter((child: any) => child.classList.contains('swiper-slide')); + + let bullet = ``; + + if (index === 0) { + bullet = ``; + } else if (index === (children.length - 1)) { + bullet = ``; + } + + return `${bullet}`; + } + }; + } + + if (this.swiperConfig.pagination === true) { + this.config.pagination = this.paginationConfig; + } else { + this.config.pagination = Object.assign({} , this.config.pagination, this.paginationConfig); + } + } + + return this.config as SwiperConfigInterface; + } + + private updateClasses(): void { + if (this.swiperSlides) { + let updateNeeded = false; + + const children = this.swiperSlides.nativeElement.children; + + for (let i = 0; i < children.length; i++) { + if (/swiper-.*/.test(children[i].className) === false) { + updateNeeded = true; + + children[i].classList.add('swiper-slide'); + } + } + + if (updateNeeded && this.directiveRef) { + this.directiveRef.update(); + } + } + + this.cdRef.detectChanges(); + } + + public onPaginationClick(index: number): void { + if (this.config && this.directiveRef && (this.config.pagination === true || + (this.config.pagination && typeof this.config.pagination === 'object' && + (!this.config.pagination.type || this.config.pagination.type === 'bullets') && + (this.config.pagination.clickable && this.config.pagination.el === '.swiper-pagination')))) + { + this.directiveRef.setIndex(index); + } + } +} diff --git a/projects/lib/src/lib/swiper.directive.ts b/projects/lib/src/lib/swiper.directive.ts new file mode 100644 index 0000000..28845b9 --- /dev/null +++ b/projects/lib/src/lib/swiper.directive.ts @@ -0,0 +1,324 @@ +import Swiper from 'swiper'; + +import { PLATFORM_ID } from '@angular/core'; +import { isPlatformBrowser } from '@angular/common'; +import { NgZone, Inject, Optional, ElementRef, Directive, + AfterViewInit, OnDestroy, DoCheck, OnChanges, Input, Output, EventEmitter, + SimpleChanges, KeyValueDiffer, KeyValueDiffers } from '@angular/core'; + +import { SWIPER_CONFIG, SwiperConfig, SwiperConfigInterface, + SwiperEvent, SwiperEvents } from './swiper.interfaces'; + +@Directive({ + selector: '[swiper]', + exportAs: 'ngxSwiper' +}) +export class SwiperDirective implements AfterViewInit, OnDestroy, DoCheck, OnChanges { + private instance: any; + + private initialIndex: number | null = null; + + private configDiff: KeyValueDiffer | null = null; + + @Input() + set index(index: number) { + if (index != null) { + this.setIndex(index); + } + } + + @Input() disabled: boolean = false; + + @Input() performance: boolean = false; + + @Input('swiper') config?: SwiperConfigInterface; + + @Output() indexChange = new EventEmitter(); + + @Output('init' ) S_INIT = new EventEmitter(); + @Output('beforeDestroy' ) S_BEFOREDESTROY = new EventEmitter(); + + @Output('scroll' ) S_SCROLL = new EventEmitter(); + @Output('progress' ) S_PROGRESS = new EventEmitter(); + @Output('keyPress' ) S_KEYPRESS = new EventEmitter(); + + @Output('resize' ) S_RESIZE = new EventEmitter(); + @Output('breakpoint' ) S_BREAKPOINT = new EventEmitter(); + @Output('zoomChange' ) S_ZOOMCHANGE = new EventEmitter(); + @Output('afterResize' ) S_AFTERRESIZE = new EventEmitter(); + @Output('beforeResize' ) S_BEFORERESIZE = new EventEmitter(); + + @Output('sliderMove' ) S_SLIDERMOVE = new EventEmitter(); + @Output('slideChange' ) S_SLIDECHANGE = new EventEmitter(); + + @Output('setTranslate' ) S_SETTRANSLATE = new EventEmitter(); + @Output('setTransition' ) S_SETTRANSITION = new EventEmitter(); + + @Output('fromEdge' ) S_FROMEDGE = new EventEmitter(); + @Output('reachEnd' ) S_REACHEND = new EventEmitter(); + @Output('reachBeginning' ) S_REACHBEGINNING = new EventEmitter(); + + @Output('autoplay' ) S_AUTOPLAY = new EventEmitter(); + @Output('autoplayStart' ) S_AUTOPLAYSTART = new EventEmitter(); + @Output('autoplayStop' ) S_AUTOPLAYSTOP = new EventEmitter(); + + @Output('imagesReady' ) S_IMAGESREADY = new EventEmitter(); + @Output('lazyImageLoad' ) S_LAZYIMAGELOAD = new EventEmitter(); + @Output('lazyImageReady' ) S_LAZYIMAGEREADY = new EventEmitter(); + + @Output('scrollDragEnd' ) S_SCROLLDRAGEND = new EventEmitter(); + @Output('scrollDragMove' ) S_SCROLLDRAGMOVE = new EventEmitter(); + @Output('scrollDragStart' ) S_SCROLLDRAGSTART = new EventEmitter(); + + @Output('navigationHide' ) S_NAVIGATIONHIDE = new EventEmitter(); + @Output('navigationShow' ) S_NAVIGATIONSHOW = new EventEmitter(); + + @Output('paginationRender' ) S_PAGINATIONRENDER = new EventEmitter(); + @Output('paginationUpdate' ) S_PAGINATIONUPDATE = new EventEmitter(); + @Output('paginationHide' ) S_PAGINATIONHIDE = new EventEmitter(); + @Output('paginationShow' ) S_PAGINATIONSHOW = new EventEmitter(); + + @Output('swiperTap' ) S_TAP = new EventEmitter(); + @Output('swiperClick' ) S_CLICK = new EventEmitter(); + @Output('swiperDoubleTap' ) S_DOUBLETAP = new EventEmitter(); + @Output('swiperTouchEnd' ) S_TOUCHEND = new EventEmitter(); + @Output('swiperTouchMove' ) S_TOUCHMOVE = new EventEmitter(); + @Output('swiperTouchStart' ) S_TOUCHSTART = new EventEmitter(); + @Output('swiperTouchMoveOpposite' ) S_TOUCHMOVEOPPOSITE = new EventEmitter(); + @Output('swiperTransitionEnd' ) S_TRANSITIONEND = new EventEmitter(); + @Output('swiperTransitionStart' ) S_TRANSITIONSTART = new EventEmitter(); + + @Output('slidePrevTransitionEnd' ) S_SLIDEPREVTRANSITIONEND = new EventEmitter(); + @Output('slidePrevTransitionStart' ) S_SLIDEPREVTRANSITIONSTART = new EventEmitter(); + @Output('slideNextTransitionEnd' ) S_SLIDENEXTTRANSITIONEND = new EventEmitter(); + @Output('slideNextTransitionStart' ) S_SLIDENEXTTRANSITIONSTART = new EventEmitter(); + @Output('slideChangeTransitionEnd' ) S_SLIDECHANGETRANSITIONEND = new EventEmitter(); + @Output('slideChangeTransitionStart' ) S_SLIDECHANGETRANSITIONSTART = new EventEmitter(); + + constructor(@Inject(PLATFORM_ID) private platformId: Object, private zone: NgZone, + private elementRef: ElementRef, private differs: KeyValueDiffers, + @Optional() @Inject(SWIPER_CONFIG) private defaults: SwiperConfigInterface) {} + + ngAfterViewInit(): void { + if (!isPlatformBrowser(this.platformId)) { + return; + } + + const params = new SwiperConfig(this.defaults); + + params.assign(this.config); // Custom configuration + + if (params.scrollbar === true) { + params.scrollbar = { + el: '.swiper-scrollbar' + }; + } + + if (params.pagination === true) { + params.pagination = { + el: '.swiper-pagination' + }; + } + + if (params.navigation === true) { + params.navigation = { + prevEl: '.swiper-button-prev', + nextEl: '.swiper-button-next' + }; + } + + if (this.disabled) { + params.allowSlidePrev = false; + params.allowSlideNext = false; + } + + if (this.initialIndex != null) { + params.initialSlide = this.initialIndex; + + this.initialIndex = null; + } + + params.on = { + slideChange: () => { + if (this.instance && this.indexChange.observers.length) { + this.emit(this.indexChange, this.instance.realIndex); + } + } + }; + + this.zone.runOutsideAngular(() => { + this.instance = new Swiper(this.elementRef.nativeElement, params); + }); + + if (params.init !== false && this.S_INIT.observers.length) { + this.emit(this.S_INIT, this.instance); + } + + // Add native Swiper event handling + SwiperEvents.forEach((eventName: SwiperEvent) => { + let swiperEvent = eventName.replace('swiper', ''); + + swiperEvent = swiperEvent.charAt(0).toLowerCase() + swiperEvent.slice(1); + + this.instance.on(swiperEvent, (...args: any[]) => { + if (args.length === 1) { + args = args[0]; + } + + const output = `S_${swiperEvent.toUpperCase()}`; + + const emitter = this[output as keyof SwiperDirective] as EventEmitter; + + if (emitter.observers.length) { + this.emit(emitter, args); + } + }); + }); + + if (!this.configDiff) { + this.configDiff = this.differs.find(this.config || {}).create(); + + this.configDiff.diff(this.config || {}); + } + } + + ngOnDestroy(): void { + if (this.instance) { + this.zone.runOutsideAngular(() => { + this.instance.destroy(true, this.instance.initialized || false); + }); + + this.instance = null; + } + } + + ngDoCheck(): void { + if (this.configDiff) { + const changes = this.configDiff.diff(this.config || {}); + + if (changes) { + this.initialIndex = this.getIndex(true); + + this.ngOnDestroy(); + + this.ngAfterViewInit(); + + this.update(); + } + } + } + + ngOnChanges(changes: SimpleChanges): void { + if (this.instance && changes['disabled']) { + if (changes['disabled'].currentValue !== changes['disabled'].previousValue) { + if (changes['disabled'].currentValue === true) { + this.zone.runOutsideAngular(() => { + this.ngOnDestroy(); + + this.ngAfterViewInit(); + }); + } else if (changes['disabled'].currentValue === false) { + this.zone.runOutsideAngular(() => { + this.ngOnDestroy(); + + this.ngAfterViewInit(); + }); + } + } + } + } + + private emit(emitter: EventEmitter, value: any): void { + if (this.performance) { + emitter.emit(value); + } else { + this.zone.run(() => emitter.emit(value)); + } + } + + public swiper(): any { + return this.instance; + } + + public init(): void { + if (this.instance) { + this.zone.runOutsideAngular(() => { + this.instance.init(); + }); + } + } + + public update(): void { + setTimeout(() => { + if (this.instance) { + this.zone.runOutsideAngular(() => { + this.instance.update(); + }); + } + }, 0); + } + + public getIndex(real?: boolean): number { + if (!this.instance) { + return this.initialIndex || 0; + } else { + return real ? this.instance.realIndex : this.instance.activeIndex; + } + } + + public setIndex(index: number, speed?: number, silent?: boolean): void { + if (!this.instance) { + this.initialIndex = index; + } else { + let realIndex = index * this.instance.params.slidesPerGroup; + + if (this.instance.params.loop) { + realIndex += this.instance.loopedSlides; + } + + this.zone.runOutsideAngular(() => { + this.instance.slideTo(realIndex, speed, !silent); + }); + } + } + + public prevSlide(speed?: number, silent?: boolean): void { + if (this.instance) { + this.zone.runOutsideAngular(() => { + this.instance.slidePrev(speed, !silent); + }); + } + } + + public nextSlide(speed?: number, silent?: boolean): void { + if (this.instance) { + this.zone.runOutsideAngular(() => { + this.instance.slideNext(speed, !silent); + }); + } + } + + public stopAutoplay(reset?: boolean): void { + if (reset) { + this.setIndex(0); + } + + if (this.instance && this.instance.autoplay) { + this.zone.runOutsideAngular(() => { + this.instance.autoplay.stop(); + }); + } + } + + public startAutoplay(reset?: boolean): void { + if (reset) { + this.setIndex(0); + } + + if (this.instance && this.instance.autoplay) { + this.zone.runOutsideAngular(() => { + this.instance.autoplay.start(); + }); + } + } +} diff --git a/projects/lib/src/lib/swiper.interfaces.ts b/projects/lib/src/lib/swiper.interfaces.ts new file mode 100644 index 0000000..50fa5d4 --- /dev/null +++ b/projects/lib/src/lib/swiper.interfaces.ts @@ -0,0 +1,555 @@ +import { InjectionToken } from '@angular/core'; + +export const SWIPER_CONFIG = new InjectionToken('SWIPER_CONFIG'); + +export type SwiperEvent = 'init' | 'beforeDestroy' | 'slideChange' | 'slideChangeTransitionStart' | + 'slideChangeTransitionEnd' | 'slideNextTransitionStart' | 'slideNextTransitionEnd' | + 'slidePrevTransitionStart' | 'slidePrevTransitionEnd' | 'swiperTransitionStart' | + 'swiperTransitionEnd' | 'swiperTouchStart' | 'swiperTouchMove' | 'swiperTouchMoveOpposite' | + 'sliderMove' | 'swiperTouchEnd' | 'swiperClick' | 'swiperTap' | 'swiperDoubleTap' | + 'imagesReady' | 'progress' | 'reachBeginning' | 'reachEnd' | 'fromEdge' | 'setTranslate' | + 'setTransition' | 'resize' | 'observerUpdate' | 'beforeLoopFix' | 'loopFix' | 'navigationHide' | + 'navigationShow' | 'paginationRender' | 'paginationUpdate' | 'paginationHide' | 'paginationShow' | + 'autoplayStart' | 'autoplayStop' | 'autoplay' | 'lazyImageLoad' | 'lazyImageReady' | + 'zoomChange' | 'scroll' | 'keyPress' | 'breakpoint' | 'beforeResize' | 'scrollbarDragEnd' | + 'scrollbarDragMove' | 'scrollbarDragStart'; + +export const SwiperEvents: SwiperEvent[] = [ + 'init', + 'beforeDestroy', + + 'scroll', + 'progress', + 'keyPress', + + 'resize', + 'loopFix', + 'breakpoint', + 'zoomChange', + 'beforeResize', + 'beforeLoopFix', + + 'sliderMove', + 'slideChange', + + 'setTranslate', + 'setTransition', + + 'fromEdge', + 'reachEnd', + 'reachBeginning', + + 'autoplay', + 'autoplayStop', + 'autoplayStart', + + 'imagesReady', + 'lazyImageLoad', + 'lazyImageReady', + + 'scrollbarDragEnd', + 'scrollbarDragMove', + 'scrollbarDragStart', + + 'navigationHide', + 'navigationShow', + + 'paginationRender', + 'paginationUpdate', + 'paginationHide', + 'paginationShow', + + 'swiperTap', + 'swiperClick', + 'swiperDoubleTap', + 'swiperTouchEnd', + 'swiperTouchMove', + 'swiperTouchStart', + 'swiperTouchMoveOpposite', + 'swiperTransitionEnd', + 'swiperTransitionStart', + + 'slideNextTransitionEnd', + 'slideNextTransitionStart', + 'slidePrevTransitionEnd', + 'slidePrevTransitionStart', + 'slideChangeTransitionEnd', + 'slideChangeTransitionStart' +]; + +export interface SwiperConfigInterface { + // Swiper parameters + init?: boolean, + updateOnWindowResize?: boolean, + initialSlide?: number, + direction?: 'horizontal' | 'vertical', + speed?: number, + setWrapperSize?: boolean, + virtualTranslate?: boolean, + width?: number, + height?: number, + autoHeight?: boolean, + roundLengths?: boolean, + nested?: boolean, + uniqueNavElements?: boolean, + effect?: 'slide' | 'fade' | 'cube' | 'coverflow' | 'flip', + runCallbacksOnInit?: boolean, + watchOverflow?: boolean, + + // CSS scroll snapOnRelease + cssMode?: boolean, + + // Slides grid + spaceBetween?: number, + slidesPerView?: number | 'auto', + slidesPerColumn?: number, + slidesPerColumnFill?: 'row' | 'column', + slidesPerGroup?: number, + centeredSlides?: boolean, + centeredSlidesBounds?: boolean, + slidesOffsetBefore?: number, + slidesOffsetAfter?: number, + normalizeSlideIndex?: boolean, + centerInsufficientSlides?: boolean, + + // Grab cursor + grabCursor?: boolean, + + // Touches + touchEventsTarget?: 'container' | 'wrapper', + touchRatio?: number, + touchAngle?: number, + simulateTouch?: boolean, + shortSwipes?: boolean, + longSwipes?: boolean, + longSwipesRatio?: number, + longSwipesMs?: number, + followFinger?: boolean, + allowTouchMove?: boolean, + threshold?: number, + touchStartPreventDefault?: boolean, + touchStartForcePreventDefault?: boolean, + touchMoveStopPropagation?: boolean, + iOSEdgeSwipeDetection?: boolean, + iOSEdgeSwipeThreshold?: number, + touchReleaseOnEdges?: boolean, + passiveListeners?: boolean, + + // Touch resistance + resistance?: boolean, + resistanceRatio?: number, + + // Swiping / no swiping + preventInteractionOnTransition?: boolean, + allowSlidePrev?: boolean, + allowSlideNext?: boolean, + noSwiping?: boolean, + noSwipingClass?: string, + noSwipingSelector?: string, + swipeHandler?: string | HTMLElement, + + // Clicks + preventClicks?: boolean, + preventClicksPropagation?: boolean, + slideToClickedSlide?: boolean, + + // Freemode + freeMode?: boolean, + freeModeMomentum?: boolean, + freeModeMomentumRatio?: number, + freeModeMomentumVelocityRatio?: number, + freeModeMomentumBounce?: boolean, + freeModeMomentumBounceRatio?: number, + freeModeMinimumVelocity?: number, + freeModeSticky?: boolean, + + // Progress + watchSlidesProgress?: boolean, + watchSlidesVisibility?: boolean, + + // Images + preloadImages?: boolean, + updateOnImagesReady?: boolean, + + // Loop + loop?: boolean, + loopAdditionalSlides?: number, + loopedSlides?: number, + loopFillGroupWithBlank?: boolean, + + // Breakpoints + breakpoints?: SwiperBreakpointsInterface, + + // Observer + observer?: boolean, + observeParents?: boolean, + observeSlideChildren?: boolean, + + // Namespace + containerModifierClass?: string, + slideClass?: string, + slideActiveClass?: string, + slideDuplicatedActiveClass?: string, + slideVisibleClass?: string, + slideDuplicateClass?: string, + slideNextClass?: string, + slideDuplicatedNextClass?: string, + slidePrevClass?: string, + slideDuplicatedPrevClass?: string, + wrapperClass?: string, + + // Effects + fadeEffect?: SwiperFadeEffectInterface, + flipEffect?: SwiperFlipEffectInterface, + cubeEffect?: SwiperCubeEffectInterface, + coverflowEffect?: SwiperCoverflowEffectInterface, + + // Components + parallax?: boolean, + + a11y?: boolean | SwiperA11YInterface, + lazy?: boolean | SwiperLazyInterface, + zoom?: boolean | SwiperZoomInterface, + thumbs?: boolean | SwiperThumbsInterface, + history?: boolean | SwiperHistoryInterface, + virtual?: boolean | SwiperVirtualInterface, + autoplay?: boolean | SwiperAutoplayInterface, + keyboard?: boolean | SwiperKeyboardInterface, + scrollbar?: boolean | SwiperScrollbarInterface, + mousewheel?: boolean | SwiperMousewheelInterface, + controller?: boolean | SwiperControllerInterface, + navigation?: boolean | SwiperNavigationInterface, + pagination?: boolean | SwiperPaginationInterface, + hashNavigation?: boolean | SwiperHashNavigationInterface +} + +export interface SwiperA11YInterface { + enabled?: boolean, + prevSlideMessage?: string, + nextSlideMessage?: string, + firstSlideMessage?: string, + lastSlideMessage?: string, + paginationBulletMessage?: string, + notificationClass?: string +} + +export interface SwiperLazyInterface { + loadPrevNext?: boolean, + loadPrevNextAmount?: number, + loadOnTransitionStart?: boolean, + elementClass?: string, + loadingClass?: string, + loadedClass?: string, + preloaderClass?: string +} + +export interface SwiperZoomInterface { + maxRatio?: number, + minRatio?: number, + toggle?: boolean, + containerClass?: string, + zoomedSlideClass?: string +} + +export interface SwiperThumbsInterface { + swiper?: any, + slideThumbActiveClass?: string, + thumbsContainerClass?: string, + multipleActiveThumbs?: boolean +} + +export interface SwiperHistoryInterface { + replaceState?: boolean, + key?: string +} + +export interface SwiperVirtualInterface { + slides?: any[], + cache?: boolean, + renderSlide?: SwiperRenderSlideFunction, + renderExternal?: SwiperRenderExternalFunction, + addSlidesBefore?: number, + addSlidesAfter?: number +} + +export interface SwiperKeyboardInterface { + enabled?: boolean, + onlyInViewport?: boolean +} + +export interface SwiperAutoplayInterface { + delay?: number, + stopOnLastSlide?: boolean, + disableOnInteraction?: boolean, + reverseDirection?: boolean, + waitForTransition?: boolean +} + +export interface SwiperScrollbarInterface { + el?: string | HTMLElement, + hide?: boolean, + draggable?: boolean, + snapOnRelease?: boolean, + dragSize?: number | 'auto', + loclClass?: string, + dragClass?: string +} + +export interface SwiperControllerInterface { + control?: any, + inverse?: boolean, + by?: 'slide' | 'container' +} + +export interface SwiperNavigationInterface { + nextEl?: string | HTMLElement, + prevEl?: string | HTMLElement, + hideOnClick?: boolean, + disabledClass?: string, + hiddenClass?: string +} + +export interface SwiperPaginationInterface { + el?: string | HTMLElement, + type?: 'bullets' | 'fraction' | 'progressbar' | 'custom', + bulletElement?: string, + dynamicBullets?: boolean, + dynamicMainBullets?: number, + hideOnClick?: boolean, + clickable?: boolean, + progressbarOpposite?: boolean, + formatFractionCurrent?: SwiperFormatFractionFunction, + formatFractionTotal?: SwiperFormatFractionFunction, + renderBullet?: SwiperRenderBulletFunction, + renderFraction?: SwiperRenderFractionFunction, + renderProgressbar?: SwiperRenderProgressbarFunction, + renderCustom?: SwiperRenderCustomFunction, + bulletClass?: string, + bulletActiveClass?: string, + modifierClass?: string, + currentClass?: string, + totalClass?: string, + hiddenClass?: string, + progressbarFillClass?: string, + clickableClass?: string, + lockClass?: string +} + +export interface SwiperMousewheelInterface { + forceToAxis?: boolean, + releaseOnEdges?: boolean, + invert?: boolean, + sensitivity?: number, + eventsTarget?: string | HTMLElement +} + +export interface SwiperHashNavigationInterface { + watchState?: boolean, + replaceState?: boolean +} + +export interface SwiperFadeEffectInterface { + crossFade?: boolean +} + +export interface SwiperFlipEffectInterface { + slideShadows?: boolean, + limitRotation?: boolean +} + +export interface SwiperCubeEffectInterface { + slideShadows?: boolean, + shadow?: boolean, + shadowOffset?: number, + shadowScale?: number +} + +export interface SwiperCoverflowEffectInterface { + slideShadows?: boolean, + rotate?: number, + stretch?: number, + depth?: number, + modifier?: number +} + +export interface SwiperBreakpointsInterface { + [size: number]: SwiperConfigInterface +} + +export class SwiperConfig implements SwiperConfigInterface { + public on?: any; + + // Swiper parameters + public init?: boolean; + public updateOnWindowResize?: boolean; + public initialSlide?: number; + public direction?: 'horizontal' | 'vertical'; + public speed?: number; + public setWrapperSize?: boolean; + public virtualTranslate?: boolean; + public width?: number; + public height?: number; + public autoHeight?: boolean; + public roundLengths?: boolean; + public nested?: boolean; + public uniqueNavElements?: boolean; + public effect?: 'slide' | 'fade' | 'cube' | 'coverflow' | 'flip'; + public runCallbacksOnInit?: boolean; + public watchOverflow?: boolean; + + // CSS scroll snapOnRelease + public cssMode?: boolean; + + // Slides grid + public spaceBetween?: number; + public slidesPerView?: number | 'auto'; + public slidesPerColumn?: number; + public slidesPerColumnFill?: 'row' | 'column'; + public slidesPerGroup?: number; + public centeredSlides?: boolean; + public centeredSlidesBounds?: boolean; + public slidesOffsetBefore?: number; + public slidesOffsetAfter?: number; + public normalizeSlideIndex?: boolean; + public centerInsufficientSlides?: boolean; + + // Grab cursor + public grabCursor?: boolean; + + // Touches + public touchEventsTarget?: 'container' | 'wrapper'; + public touchRatio?: number; + public touchAngle?: number; + public simulateTouch?: boolean; + public shortSwipes?: boolean; + public longSwipes?: boolean; + public longSwipesRatio?: number; + public longSwipesMs?: number; + public followFinger?: boolean; + public allowTouchMove?: boolean; + public threshold?: number; + public touchStartPreventDefault?: boolean; + public touchStartForcePreventDefault?: boolean; + public touchMoveStopPropagation?: boolean; + public iOSEdgeSwipeDetection?: boolean; + public iOSEdgeSwipeThreshold?: number; + public touchReleaseOnEdges?: boolean; + public passiveListeners?: boolean; + + // Touch resistance + public resistance?: boolean; + public resistanceRatio?: number; + + // Swiping / no swiping + public preventInteractionOnTransition?: boolean; + public allowSlidePrev?: boolean; + public allowSlideNext?: boolean; + public noSwiping?: boolean; + public noSwipingClass?: string; + public noSwipingSelector?: string; + public swipeHandler?: string | HTMLElement; + + // Clicks + public preventClicks?: boolean; + public preventClicksPropagation?: boolean; + public slideToClickedSlide?: boolean; + + // Freemode + public freeMode?: boolean; + public freeModeMomentum?: boolean; + public freeModeMomentumRatio?: number; + public freeModeMomentumVelocityRatio?: number; + public freeModeMomentumBounce?: boolean; + public freeModeMomentumBounceRatio?: number; + public freeModeMinimumVelocity?: number; + public freeModeSticky?: boolean; + + // Progress + public watchSlidesProgress?: boolean; + public watchSlidesVisibility?: boolean; + + // Images + public preloadImages?: boolean; + public updateOnImagesReady?: boolean; + + // Loop + public loop?: boolean; + public loopAdditionalSlides?: number; + public loopedSlides?: number; + public loopFillGroupWithBlank?: boolean; + + // Breakpoints + public breakpoints?: any; + + // Observer + public observer?: boolean; + public observeParents?: boolean; + public observeSlideChildren?: boolean; + + // Namespace + public containerModifierClass?: string; + public slideClass?: string; + public slideActiveClass?: string; + public slideDuplicatedActiveClass?: string; + public slideVisibleClass?: string; + public slideDuplicateClass?: string; + public slideNextClass?: string; + public slideDuplicatedNextClass?: string; + public slidePrevClass?: string; + public slideDuplicatedPrevClass?: string; + public wrapperClass?: string; + + // Effects + public fadeEffect?: any; + public flipEffect?: any; + public cubeEffect?: any; + public coverflowEffect?: any; + + // Components + public parallax?: boolean; + + public a11y?: boolean | any; + public lazy?: boolean | any; + public zoom?: boolean | any; + public history?: boolean | any; + public virtual?: boolean | any; + public autoplay?: boolean | any; + public keyboard?: boolean | any; + public scrollbar?: boolean | any; + public mousewheel?: boolean | any; + public controller?: boolean | any; + public navigation?: boolean | any; + public pagination?: boolean | any; + public hashNavigation?: boolean | any; + + constructor(config: SwiperConfigInterface = {}) { + this.assign(config); + } + + assign(config: SwiperConfigInterface | any = {}, target?: any) { + target = target || this; + + for (const key in config) { + if (config[key] != null && !Array.isArray(config[key]) && typeof config[key] === 'object' && + (typeof HTMLElement === 'undefined' || !(config[key] instanceof HTMLElement))) + { + target[key] = {}; + + this.assign(config[key], target[key]); + } else { + target[key] = config[key]; + } + } + } +} + +export type SwiperFormatFractionFunction = (fraction: number) => number; + +export type SwiperRenderSlideFunction = (slide: any, index: number) => HTMLElement; +export type SwiperRenderExternalFunction = (data: any) => void; + +export type SwiperRenderCustomFunction = (swiper: any, current: number, total: number) => string; + +export type SwiperRenderBulletFunction = (index: number, className: string) => string; +export type SwiperRenderFractionFunction = (currentClass: string, totalClass: string) => string; +export type SwiperRenderProgressbarFunction = (progressbarClass: string) => string; diff --git a/projects/lib/src/lib/swiper.module.ts b/projects/lib/src/lib/swiper.module.ts new file mode 100644 index 0000000..cabaaa1 --- /dev/null +++ b/projects/lib/src/lib/swiper.module.ts @@ -0,0 +1,14 @@ +import { NgModule } from '@angular/core'; + +import { CommonModule } from '@angular/common'; + +import { SwiperComponent } from './swiper.component'; +import { SwiperDirective } from './swiper.directive'; + +@NgModule({ + imports: [ CommonModule ], + declarations: [ SwiperComponent, SwiperDirective ], + exports: [ CommonModule, SwiperComponent, SwiperDirective ] +}) +export class SwiperModule { +} diff --git a/projects/lib/src/public-api.ts b/projects/lib/src/public-api.ts new file mode 100644 index 0000000..9f060c3 --- /dev/null +++ b/projects/lib/src/public-api.ts @@ -0,0 +1,38 @@ +export { SwiperComponent } from './lib/swiper.component'; +export { SwiperDirective } from './lib/swiper.directive'; + +export { + SWIPER_CONFIG, + + SwiperConfig, + SwiperConfigInterface, + SwiperBreakpointsInterface, + + SwiperA11YInterface, + SwiperLazyInterface, + SwiperZoomInterface, + SwiperHistoryInterface, + SwiperVirtualInterface, + SwiperAutoplayInterface, + SwiperKeyboardInterface, + SwiperScrollbarInterface, + SwiperMousewheelInterface, + SwiperControllerInterface, + SwiperNavigationInterface, + SwiperPaginationInterface, + SwiperHashNavigationInterface, + + SwiperFadeEffectInterface, + SwiperFlipEffectInterface, + SwiperCubeEffectInterface, + SwiperCoverflowEffectInterface, + + SwiperRenderSlideFunction, + SwiperRenderCustomFunction, + SwiperRenderBulletFunction, + SwiperRenderExternalFunction, + SwiperRenderFractionFunction, + SwiperRenderProgressbarFunction +} from './lib/swiper.interfaces'; + +export { SwiperModule } from './lib/swiper.module'; diff --git a/projects/lib/stylelint.json b/projects/lib/stylelint.json new file mode 100644 index 0000000..f76cdce --- /dev/null +++ b/projects/lib/stylelint.json @@ -0,0 +1,230 @@ +{ + "plugins": [ + "stylelint-order" + ], + "extends": [ + "stylelint-config-standard" + ], + "rules": { + "color-hex-case": "lower", + "color-no-invalid-hex": true, + + "font-family-no-missing-generic-family-keyword": null, + "function-calc-no-unspaced-operator": true, + "function-comma-space-after": "always-single-line", + "function-comma-space-before": "never", + "function-name-case": "lower", + "function-url-quotes": "always", + "function-whitespace-after": "always", + + "number-leading-zero": "always", + "number-no-trailing-zeros": true, + "length-zero-no-unit": true, + + "string-no-newline": true, + "string-quotes": "single", + + "unit-case": "lower", + "unit-no-unknown": true, + "unit-whitelist": [ + "px", + "%", + "deg", + "ms", + "em" + ], + + "value-list-comma-space-after": "always-single-line", + "value-list-comma-space-before": "never", + + "shorthand-property-no-redundant-values": true, + + "property-case": "lower", + + "at-rule-empty-line-before": [ + "always", + { + "ignore": [ + "blockless-after-blockless" + ] + } + ], + + "at-rule-no-unknown": null, + + "declaration-block-no-duplicate-properties": true, + "declaration-block-trailing-semicolon": "always", + "declaration-block-single-line-max-declarations": 1, + "declaration-block-semicolon-space-before": "never", + "declaration-block-semicolon-space-after": "always-single-line", + "declaration-block-semicolon-newline-before": "never-multi-line", + "declaration-block-semicolon-newline-after": "always-multi-line", + + "block-closing-brace-newline-after": "always", + "block-closing-brace-newline-before": "always-multi-line", + "block-no-empty": true, + "block-opening-brace-newline-after": "always-multi-line", + "block-opening-brace-space-before": "always-multi-line", + + "selector-attribute-brackets-space-inside": "never", + "selector-attribute-operator-space-after": "never", + "selector-attribute-operator-space-before": "never", + "selector-combinator-space-after": "always", + "selector-combinator-space-before": "always", + "selector-pseudo-class-case": "lower", + "selector-pseudo-class-parentheses-space-inside": "never", + "selector-pseudo-element-case": "lower", + "selector-pseudo-element-colon-notation": "double", + "selector-pseudo-element-no-unknown": true, + "selector-type-no-unknown": null, + "selector-type-case": "lower", + "selector-max-id": 0, + + "declaration-empty-line-before": null, + + "no-descending-specificity": null, + + "order/properties-order": [ + [ + { + "properties": [ + "content", + "direction" + ] + }, { + "emptyLineBefore": "always", + "properties": [ + "float", + "position", + "z-index", + "top", + "right", + "bottom", + "left" + ] + }, { + "emptyLineBefore": "always", + "properties": [ + "visibility", + "opacity", + "display", + "overflow", + "box-sizing", + "flex", + "flex-basis", + "flex-direction", + "flex-flow", + "flex-grow", + "flex-shrink", + "flex-wrap", + "align-self", + "align-items", + "align-content", + "justify-content", + "order", + "width", + "height", + "min-width", + "min-height", + "max-width", + "max-height", + "padding", + "padding-top", + "padding-right", + "padding-bottom", + "padding-left", + "margin", + "margin-top", + "margin-right", + "margin-bottom", + "margin-left", + "border", + "border-top", + "border-right", + "border-bottom", + "border-left", + "border-width", + "border-top-width", + "border-right-width", + "border-bottom-width", + "border-left-width", + "border-style", + "border-top-style", + "border-right-style", + "border-bottom-style", + "border-left-style", + "border-color", + "border-top-color", + "border-right-color", + "border-bottom-color", + "border-left-color", + "border-radius", + "border-top-left-radius", + "border-top-right-radius", + "border-bottom-right-radius", + "border-bottom-left-radius", + "outline" + ] + }, { + "emptyLineBefore": "always", + "properties": [ + "cursor", + "resize", + "user-select", + "touch-action", + "pointer-events", + "font-size", + "font-style", + "font-weight", + "font-family", + "line-height", + "white-space", + "text-align", + "text-shadow", + "text-decoration", + "text-transform", + "text-overflow", + "letter-spacing", + "list-style-type", + "object-fit", + "vertical-align", + "color", + "background", + "background-size", + "background-color", + "background-image", + "background-repeat", + "background-position", + "background-attachment", + "box-shadow" + ] + }, { + "emptyLineBefore": "always", + "properties": [ + "filter", + "animation", + "animation-name", + "animation-delay", + "animation-duration", + "animation-direction", + "animation-fill-mode", + "animation-play-state", + "animation-iteration-count", + "animation-timing-function", + "transform", + "transform-style", + "transform-origin", + "transition", + "transition-delay", + "transition-duration", + "transition-property", + "transition-timing-function" + ] + } + ], + { + "unspecified": "bottomAlphabetical" + } + ] + } +} diff --git a/projects/lib/tsconfig.json b/projects/lib/tsconfig.json new file mode 100644 index 0000000..9670a9d --- /dev/null +++ b/projects/lib/tsconfig.json @@ -0,0 +1,24 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "../../out-tsc/lib", + "target": "es2015", + "declaration": true, + "inlineSources": true, + "types": [], + "lib": [ + "dom", + "es2018" + ] + }, + "angularCompilerOptions": { + "skipTemplateCodegen": true, + "strictMetadataEmit": true, + "enableIvy": false, + "enableResourceInlining": true + }, + "exclude": [ + "src/test.ts", + "**/*.spec.ts" + ] +} diff --git a/projects/lib/tslint.json b/projects/lib/tslint.json new file mode 100644 index 0000000..0946f20 --- /dev/null +++ b/projects/lib/tslint.json @@ -0,0 +1,3 @@ +{ + "extends": "../../tslint.json" +}