├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .prettierignore ├── .prettierrc ├── CHANGELOG.md ├── LICENSE ├── README.md ├── babel.config.js ├── build └── rollup.config.js ├── demo ├── .browserslistrc ├── .env.development.android ├── .env.development.ios ├── .env.development.web ├── .env.production.android ├── .env.production.ios ├── .env.production.web ├── .eslintrc.js ├── .gitignore ├── README.md ├── app │ ├── App.vue │ ├── App_Resources │ │ ├── Android │ │ │ ├── AndroidManifest.xml │ │ │ ├── app.gradle │ │ │ ├── drawable-hdpi │ │ │ │ ├── background.png │ │ │ │ ├── icon.png │ │ │ │ └── logo.png │ │ │ ├── drawable-ldpi │ │ │ │ ├── background.png │ │ │ │ ├── icon.png │ │ │ │ └── logo.png │ │ │ ├── drawable-mdpi │ │ │ │ ├── background.png │ │ │ │ ├── icon.png │ │ │ │ └── logo.png │ │ │ ├── drawable-nodpi │ │ │ │ └── splash_screen.xml │ │ │ ├── drawable-xhdpi │ │ │ │ ├── background.png │ │ │ │ ├── icon.png │ │ │ │ └── logo.png │ │ │ ├── drawable-xxhdpi │ │ │ │ ├── background.png │ │ │ │ ├── icon.png │ │ │ │ └── logo.png │ │ │ ├── drawable-xxxhdpi │ │ │ │ ├── background.png │ │ │ │ ├── icon.png │ │ │ │ └── logo.png │ │ │ ├── values-v21 │ │ │ │ ├── colors.xml │ │ │ │ ├── strings.xml │ │ │ │ └── styles.xml │ │ │ └── values │ │ │ │ ├── colors.xml │ │ │ │ ├── strings.xml │ │ │ │ └── styles.xml │ │ └── iOS │ │ │ ├── Assets.xcassets │ │ │ ├── AppIcon.appiconset │ │ │ │ ├── Contents.json │ │ │ │ ├── icon-1024.png │ │ │ │ ├── icon-29.png │ │ │ │ ├── icon-29@2x.png │ │ │ │ ├── icon-29@3x.png │ │ │ │ ├── icon-40.png │ │ │ │ ├── icon-40@2x.png │ │ │ │ ├── icon-40@3x.png │ │ │ │ ├── icon-60@2x.png │ │ │ │ ├── icon-60@3x.png │ │ │ │ ├── icon-76.png │ │ │ │ ├── icon-76@2x.png │ │ │ │ └── icon-83.5@2x.png │ │ │ ├── Contents.json │ │ │ ├── LaunchImage.launchimage │ │ │ │ ├── Contents.json │ │ │ │ ├── Default-1125h.png │ │ │ │ ├── Default-568h@2x.png │ │ │ │ ├── Default-667h@2x.png │ │ │ │ ├── Default-736h@3x.png │ │ │ │ ├── Default-Landscape-X.png │ │ │ │ ├── Default-Landscape.png │ │ │ │ ├── Default-Landscape@2x.png │ │ │ │ ├── Default-Landscape@3x.png │ │ │ │ ├── Default-Portrait.png │ │ │ │ ├── Default-Portrait@2x.png │ │ │ │ ├── Default.png │ │ │ │ └── Default@2x.png │ │ │ ├── LaunchScreen.AspectFill.imageset │ │ │ │ ├── Contents.json │ │ │ │ ├── LaunchScreen-AspectFill.png │ │ │ │ └── LaunchScreen-AspectFill@2x.png │ │ │ └── LaunchScreen.Center.imageset │ │ │ │ ├── Contents.json │ │ │ │ ├── LaunchScreen-Center.png │ │ │ │ └── LaunchScreen-Center@2x.png │ │ │ ├── Info.plist │ │ │ ├── LaunchScreen.storyboard │ │ │ └── build.xcconfig │ ├── assets │ │ └── demo-screenshot.png │ ├── main.ts │ ├── package.json │ └── views │ │ └── Home.vue ├── babel.config.js ├── nsconfig.json ├── package-lock.json ├── package.json ├── postcss.config.js ├── public │ ├── favicon.ico │ └── index.html ├── tsconfig.json ├── types │ ├── globals.d.ts │ ├── shims-tsx.d.ts │ └── shims-vue.d.ts ├── vue.config.js └── webpack.config.js ├── dist ├── nativescript-vue-shadow.esm.js ├── nativescript-vue-shadow.js └── nativescript-vue-shadow.umd.js ├── jest.config.js ├── package-lock.json ├── package.json ├── src ├── common │ ├── android-data.model.ts │ ├── elevation.enum.ts │ ├── index.ts │ ├── ios-data.model.ts │ ├── shadow.ts │ └── shape.enum.ts ├── index.ts └── vue-shadow.ts ├── tests └── unit │ ├── .eslintrc.js │ ├── example.spec.ts │ └── plugin.spec.ts ├── tsconfig.json └── types ├── common ├── android-data.model.d.ts ├── elevation.enum.d.ts ├── index.d.ts ├── ios-data.model.d.ts ├── shadow.d.ts └── shape.enum.d.ts ├── index.d.ts ├── vue-shadow.d.ts └── vue-shims.d.ts /.eslintignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | node_modules/ -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // https://eslint.org/docs/user-guide/configuring#using-configuration-files-1 3 | root: true, 4 | 5 | // https://eslint.org/docs/user-guide/configuring#specifying-environments 6 | env: { 7 | browser: true, 8 | node: true 9 | }, 10 | 11 | // https://eslint.org/docs/user-guide/configuring#specifying-parser 12 | parser: 'vue-eslint-parser', 13 | // https://vuejs.github.io/eslint-plugin-vue/user-guide/#faq 14 | parserOptions: { 15 | parser: '@typescript-eslint/parser', 16 | ecmaVersion: 2017, 17 | sourceType: 'module', 18 | project: './tsconfig.json' 19 | }, 20 | 21 | // https://eslint.org/docs/user-guide/configuring#extending-configuration-files 22 | // order matters: from least important to most important in terms of overriding 23 | // Prettier + Vue: https://medium.com/@gogl.alex/how-to-properly-set-up-eslint-with-prettier-for-vue-or-nuxt-in-vscode-e42532099a9c 24 | extends: [ 25 | 'eslint:recommended', 26 | 'plugin:@typescript-eslint/recommended', 27 | 'plugin:vue/recommended', 28 | 'prettier', 29 | 'prettier/vue', 30 | 'prettier/@typescript-eslint' 31 | ], 32 | 33 | 34 | // https://eslint.org/docs/user-guide/configuring#configuring-plugins 35 | plugins: ['vue', '@typescript-eslint'], 36 | 37 | 38 | 39 | "rules": { 40 | "import/extensions": 0, 41 | "global-require": 0, 42 | "eol-last": 0, 43 | "no-param-reassign": 0, 44 | "object-curly-newline": 0, 45 | "no-plusplus": 0, 46 | "no-console": "off", 47 | "no-implicity-any": "off", 48 | "no-explicity-any": "off", 49 | "vue/valid-template-root": "off", 50 | "max-len": [ 51 | 2, 52 | { 53 | "code": 160 54 | } 55 | ], 56 | "prefer-destructuring": [ 57 | 2, 58 | { 59 | "object": true, 60 | "array": false 61 | } 62 | ], 63 | "@typescript-eslint/no-empty-interface": 1, 64 | // https://github.com/typescript-eslint/typescript-eslint/issues/103 65 | "@typescript-eslint/no-parameter-properties": 0 66 | }, 67 | 68 | } 69 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | 63 | .DS_Store 64 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist/ -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 200, 3 | "singleQuote": true, 4 | "trailingComma": "none", 5 | "tabWidth": 2, 6 | "semicolons": true, 7 | "bracketSpacing": true, 8 | "arrowParens": "always", 9 | "useTabs": false 10 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | // TODO: release log here ... 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Gary Gambill 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Nativescript-Vue Shadow Directive Plugin ![apple](https://cdn3.iconfinder.com/data/icons/picons-social/57/16-apple-32.png) ![android](https://cdn4.iconfinder.com/data/icons/logos-3/228/android-32.png) 2 | 3 | [![NPM version][npm-image]][npm-url] 4 | [![Downloads][downloads-image]][npm-url] 5 | [![Twitter Follow][twitter-image]][twitter-url] 6 | 7 | [npm-image]: http://img.shields.io/npm/v/nativescript-vue-shadow.svg 8 | [npm-url]: https://npmjs.org/package/nativescript-vue-shadow 9 | [downloads-image]: http://img.shields.io/npm/dt/nativescript-vue-shadow.svg 10 | [twitter-image]: https://img.shields.io/twitter/follow/Jawa_the_Hutt.svg?style=social&label=Jawa_the_hutt 11 | [twitter-url]: https://twitter.com/Jawa_the_Hutt 12 | 13 | This repo is a port to Nativescript-Vue of @JoshDSommer's [NativeScript Angular Shadow Directive project](https://github.com/JoshDSommer/nativescript-ngx-shadow). As such, a good portion of the code is inspired by it, but many changes were made to fit into how Vue does Directives. 14 | 15 | ## Installation 16 | 17 | From the command prompt go to your app's root folder and execute: 18 | 19 | ``` bash 20 | npm install nativescript-vue-shadow 21 | ``` 22 | 23 | ## Demo 24 | 25 | ![Screenshot](/demo/app/assets/demo-screenshot.png) 26 | 27 | ### How to use it 28 | 29 | This is a Vue directive to make your life easier when it comes to using native shadows with Nativescript-Vue. 30 | 31 | Shadows are a very important part of [Material design specification](https://material.io/). 32 | It brings up the [concept of elevation](https://material.io/guidelines/material-design/elevation-shadows.html), which implies in the natural human way of perceiving objects raising up from the surface. 33 | 34 | With this directive, you won't have to worry about all the aspects regarding shadowing on Android and on iOS. On the other hand, if you care about some of the details, you will still be able to provide certain extra attributes and they will superseed the default ones. 35 | 36 | However, running this on Android you will require the SDK to be greater or equal than 21 (Android 5.0 Lollipop or later), otherwise shadows will simply not be shown. There should be no problem running this on any version of iOS. 37 | 38 | #### Import the Directive into main.js 39 | 40 | ```typescript 41 | import NSVueShadow from 'nativescript-vue-shadow' 42 | Vue.use(NSVueShadow) 43 | ``` 44 | 45 | #### Use in your view or component 46 | 47 | Simple attribute for `v-shadow`: 48 | 49 | ```html 50 | 51 | ``` 52 | 53 | You can property bind it in your `template` tag. This can be a `string`, `number` or `object` ( [AndroidData](https://github.com/jawa-the-hutt/nativescript-vue-shadow/blob/master/src/common/android-data.model.ts) \| [IOSData](https://github.com/jawa-the-hutt/nativescript-vue-shadow/blob/master/src/common/ios-data.model.ts) ): 54 | 55 | ```html 56 | 57 | ``` 58 | 59 | Then in your `script` tag you can do something like this where we bind to the object: 60 | 61 | ```typescript 62 | import { AndroidData, ShapeEnum } from "nativescript-vue-shadow"; 63 | // ... 64 | export default class MyComponent extends Vue { 65 | private myCustomData: AndroidData = { 66 | elevation: 6, 67 | bgcolor: "#ff1744", 68 | shape: ShapeEnum.OVAL 69 | }; 70 | // ... 71 | } 72 | ``` 73 | 74 | You can also provide details directly in your markup by using the `v-shadow` directive with an explicit object ( [AndroidData](https://github.com/jawa-the-hutt/nativescript-vue-shadow/blob/master/src/common/android-data.model.ts) \| [IOSData](https://github.com/jawa-the-hutt/nativescript-vue-shadow/blob/master/src/common/ios-data.model.ts) ): 75 | 76 | ```html 77 | 78 | ``` 79 | 80 | There are a couple of platform specific attributes you might want to use to customize your view. Bear in mind some of them clash with CSS styles applied to the same views. When it happens, the default behaviour on Android is the original HTML/CSS styles are lost in favor of the ones provided by this directive. On iOS, on the other hand, HTML/CSS pre-existent styles are regarded, consequently the shadow might not be applied. 81 | 82 | The tip is avoid applying things like **background color** and **border radius** to the same view you intend to apply this directive (Note: this is now supported). 83 | 84 | ### List of attributes 85 | 86 | The table below list and describes all possible attributes as well as show which platform supports each one of them: 87 | 88 | | Attribute | Type | Default | Platform | Description | 89 | | -------------------- | ---------------- | -------- | ---------- |--------------- | 90 | | elevation | number \| string | | both | Determines the elevation of the view from the surface. It does all shadow related calculations. You might want to have a look at [this enum](https://github.com/jawa-the-hutt/nativescript-vue-shadow/blob/master/src/common/elevation.enum.ts) of standard material design elevations. FYI, it's calculated in DIPs (or DPs, _density independent pixels_) on Android, or PTs (_points_) on iOS. | 91 | | pressedElevation | number \| string | | Android | Determines the view's elevation when on pressed state. | 92 | | shape | string => `'RECTANGLE'` \| `'OVAL'` \| `'RING'` \| `'LINE'` | `'RECTANGLE'` | Android | Determines the shape of the view and overrides its format styles. | 93 | | bgcolor | string (#RGB) | | Android | Determines view's background color and overrides its previous background. If not set, the previous background is used. **NOTE:** setting the background to transparent is known to cause issues on Android (the shadow may overlap the background) | 94 | | cornerRadius | number | | Android | Determines view's corner radius _(CSS border-radius)_ and overrides its previous style. If this is not set, the view's CSS border-radius are used. FYI, it's calculated in DIPs (or DPs, _density independent pixels_). | 95 | | translationZ | number | | Android | Determines an extra distance (in DIP) to the surface. | 96 | | pressedTranslationZ | number | | Android | Determines an extra distance (in DIP) to the surface when the view is in the pressed state. | 97 | | forcePressAnimation | boolean | false | Android | By default, if a view has a StateAnimator, it is overwritten by an animator that raises the View on press. Setting this to true will always define this new animator, essentially making any clickable View work as a button. | 98 | | maskToBounds | boolean | false | iOS | Determines whether the shadow will be limited to the view margins. | 99 | | shadowColor | string (#RGB) | | iOS | Determines shadow color. Shadow won't be applied if the view already has background. | 100 | | shadowOffset | number | | iOS | Determines the distance in points (only on Y axis) of the shadow. Negative value shows the shadow above the view. | 101 | | shadowOpacity | number | | iOS | From 0 to 1. Determines the opacity level of the shadow. | 102 | | shadowRadius | number | | iOS | Determines the blurring effect in points of the shadow. The higher the more blurred. | 103 | | useShadowPath | boolean | true | iOS | Determines whether to use shadowPath to render the shadow. Setting this to false negatively impacts performance. | 104 | | rasterize | boolean | false | iOS | Determines whether the view should be rasterized. Activating this will increase memory usage, as rasterized views are stored in memory, but will massively improve performance. | 105 | 106 | ## Pre-defined elevations 107 | 108 | If you want to be consistent with the Material Design specification but you're sick of trying to memorize which elevation your view should have. We've put together a list of pre-defined elevations: 109 | 110 | - SWITCH: 1 111 | - CARD_RESTING: 2 112 | - RAISED_BUTTON_RESTING: 2 113 | - SEARCH_BAR_RESTING: 2 114 | - REFRESH_INDICADOR: 3 115 | - SEARCH_BAR_SCROLLED: 3 116 | - APPBAR: 4 117 | - FAB_RESTING: 6 118 | - SNACKBAR: 6 119 | - BOTTOM_NAVIGATION_BAR: 8 120 | - MENU: 8 121 | - CARD_PICKED_UP: 8, 122 | - RAISED_BUTTON_PRESSED: 8 123 | - SUBMENU_LEVEL1: 9 124 | - SUBMENU_LEVEL2: 10 125 | - SUBMENU_LEVEL3: 11 126 | - SUBMENU_LEVEL4: 12 127 | - SUBMENU_LEVEL5: 13 128 | - FAB_PRESSED: 12 129 | - NAV_DRAWER: 16 130 | - RIGHT_DRAWER: 16 131 | - MODAL_BOTTOM_SHEET: 16 132 | - DIALOG: 24 133 | - PICKER: 24 134 | 135 | If you don't even want to check it out every time you have to shadow an element, just import the `Elevation` enum and enjoy :) 136 | 137 | #### Component 138 | 139 | ```typescript 140 | import { Elevation } from "nativescript-vue-shadow"; 141 | 142 | export default class MyComponent extends Vue { 143 | // ... 144 | private elevation: number = Elevation.SNACKBAR; 145 | // ... 146 | } 147 | ``` 148 | 149 | ### Override Android default StateListAnimator 150 | 151 | Android buttons are split into three categories: floating, raised and flat. Different from labels and other ui elements, each button category has its own state animator. So, when buttons are tapped, Android does affect their elevation (and z translation) in a way that Angular is not notified. At the end of tap animation, buttons get back to resting defaults (i.e. raised button's `elevation` at 2dp and `translationZ` at 0) overriding the shadow stablished by this plugin. 152 | 153 | This plugin replaces the default `StateListAnimator` with one that gets back to the values you provide for `elevation` and `translationZ`. 154 | 155 | Feel free to fill submit a PR if you want the flexibility of defining your own `StateListAnimator`. The motivation so far was simply put this plugin to work with buttons without changing the original state once they are clicked. 156 | 157 | It's also possible to set this `StateListAnimator` to any View, making it behave like a button. 158 | 159 | ## Plugin Development Work Flow 160 | 161 | - Clone repository to your machine. 162 | - Run `npm install` in base directory of project 163 | - Change to Demo directory and then run `npm install` 164 | - Run and deploy to your device or emulator with `npm run serve:android` or `npm run serve:ios`. (use `debug:android` or `debug:ios` to attach to devtools) 165 | 166 | ## Changelog 167 | 168 | - 0.1.0 Initial implementation 169 | 170 | ## License 171 | 172 | MIT License -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | target: "node" 7 | }, 8 | "@babel/preset-es2015" 9 | ] 10 | ], 11 | plugins: ["@babel/plugin-syntax-dynamic-import"], 12 | retainLines: true, 13 | comments: true 14 | }; 15 | -------------------------------------------------------------------------------- /build/rollup.config.js: -------------------------------------------------------------------------------- 1 | // rollup.config.js 2 | import vue from "rollup-plugin-vue"; 3 | import commonjs from "rollup-plugin-commonjs"; 4 | import replace from "rollup-plugin-replace"; 5 | import { terser } from "rollup-plugin-terser"; 6 | import typescript from "rollup-plugin-typescript2"; 7 | import minimist from "minimist"; 8 | import resolve from "rollup-plugin-node-resolve"; 9 | 10 | const argv = minimist(process.argv.slice(2)); 11 | 12 | const baseConfig = { 13 | input: "src/index.ts", 14 | inlineDynamicImports: true, 15 | plugins: [ 16 | replace({ 17 | "process.env.NODE_ENV": JSON.stringify("production") 18 | }), 19 | resolve(), 20 | commonjs(), 21 | typescript({ 22 | tsconfig: "tsconfig.json", 23 | useTsconfigDeclarationDir: true, 24 | objectHashIgnoreUnknownHack: true, 25 | clean: true 26 | }), 27 | vue({ 28 | css: true, 29 | compileTemplate: true, 30 | template: { 31 | isProduction: true 32 | } 33 | }) 34 | ] 35 | }; 36 | 37 | // UMD/IIFE shared settings: externals and output.globals 38 | // Refer to https://rollupjs.org/guide/en#output-globals for details 39 | const external = [ 40 | // list external dependencies, exactly the way it is written in the import statement. 41 | // eg. 'jquery' 42 | "nativescript-vue", 43 | "tns-core-modules/color", 44 | "tns-core-modules/platform", 45 | "tns-core-modules/ui/page/page", 46 | "tns-core-modules/ui/core/weak-event-listener", 47 | "'tns-core-modules/ui/layouts/stack-layout" 48 | ]; 49 | const globals = { 50 | // Provide global variable names to replace your external imports 51 | // eg. jquery: '$' 52 | "nativescript-vue": 'vue', 53 | "tns-core-modules/color": 'color', 54 | "tns-core-modules/platform": 'platform', 55 | "tns-core-modules/ui/page/page": 'page', 56 | "tns-core-modules/ui/core/weak-event-listener" : 'weakEventListener', 57 | }; 58 | 59 | // Customize configs for individual targets 60 | const buildFormats = []; 61 | if (!argv.format || argv.format === "es") { 62 | const esConfig = { 63 | ...baseConfig, 64 | external, 65 | output: { 66 | file: "dist/nativescript-vue-shadow.esm.js", 67 | format: "esm", 68 | exports: "named" 69 | }, 70 | plugins: [ 71 | ...baseConfig.plugins 72 | // terser({ 73 | // output: { 74 | // ecma: 6 75 | // } 76 | // }) 77 | ] 78 | }; 79 | buildFormats.push(esConfig); 80 | } 81 | 82 | if (!argv.format || argv.format === "umd") { 83 | const umdConfig = { 84 | ...baseConfig, 85 | external, 86 | output: { 87 | compact: true, 88 | file: "dist/nativescript-vue-shadow.umd.js", 89 | format: "umd", 90 | name: "NativescriptVueshadow", 91 | exports: "named", 92 | globals 93 | }, 94 | plugins: [ 95 | ...baseConfig.plugins 96 | // terser({ 97 | // output: { 98 | // ecma: 6 99 | // } 100 | // }) 101 | ] 102 | }; 103 | buildFormats.push(umdConfig); 104 | } 105 | 106 | if (!argv.format || argv.format === "iife") { 107 | const unpkgConfig = { 108 | ...baseConfig, 109 | external, 110 | output: { 111 | compact: true, 112 | file: "dist/nativescript-vue-shadow.js", 113 | format: "iife", 114 | name: "NativescriptVueshadow", 115 | exports: "named", 116 | globals 117 | }, 118 | plugins: [ 119 | ...baseConfig.plugins 120 | // terser({ 121 | // output: { 122 | // ecma: 5 123 | // } 124 | // }) 125 | ] 126 | }; 127 | buildFormats.push(unpkgConfig); 128 | } 129 | 130 | // Export config 131 | export default buildFormats; 132 | -------------------------------------------------------------------------------- /demo/.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | not ie <= 8 4 | -------------------------------------------------------------------------------- /demo/.env.development.android: -------------------------------------------------------------------------------- 1 | NODE_ENV=development 2 | VUE_APP_PLATFORM=android 3 | VUE_APP_MODE=native -------------------------------------------------------------------------------- /demo/.env.development.ios: -------------------------------------------------------------------------------- 1 | NODE_ENV=development 2 | VUE_APP_PLATFORM=ios 3 | VUE_APP_MODE=native -------------------------------------------------------------------------------- /demo/.env.development.web: -------------------------------------------------------------------------------- 1 | NODE_ENV=development 2 | VUE_APP_PLATFORM=web 3 | VUE_APP_MODE=web -------------------------------------------------------------------------------- /demo/.env.production.android: -------------------------------------------------------------------------------- 1 | NODE_ENV=production 2 | VUE_APP_PLATFORM=android 3 | VUE_APP_MODE=native -------------------------------------------------------------------------------- /demo/.env.production.ios: -------------------------------------------------------------------------------- 1 | NODE_ENV=production 2 | VUE_APP_PLATFORM=ios 3 | VUE_APP_MODE=native -------------------------------------------------------------------------------- /demo/.env.production.web: -------------------------------------------------------------------------------- 1 | NODE_ENV=production 2 | VUE_APP_PLATFORM=web 3 | VUE_APP_MODE=web -------------------------------------------------------------------------------- /demo/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | 4 | env: { 5 | node: true 6 | }, 7 | 8 | extends: ['plugin:vue/essential', '@vue/prettier', '@vue/typescript'], 9 | 10 | rules: { 11 | 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off', 12 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off' 13 | }, 14 | 15 | parserOptions: { 16 | parser: '@typescript-eslint/parser' 17 | }, 18 | 19 | globals: { 20 | TNS_APP_MODE: true, 21 | TNS_APP_PLATFORM: true 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /demo/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw? 22 | 23 | # NativeScript application 24 | hooks 25 | platforms 26 | ./webpack.config.js -------------------------------------------------------------------------------- /demo/README.md: -------------------------------------------------------------------------------- 1 | # demo 2 | 3 | ## Project setup 4 | ``` 5 | npm install 6 | ``` 7 | 8 | ### Compiles and hot-reloads for development 9 | ``` 10 | npm run serve 11 | ``` 12 | 13 | ### Compiles and minifies for production 14 | ``` 15 | npm run build 16 | ``` 17 | 18 | ### Run your tests 19 | ``` 20 | npm run test 21 | ``` 22 | 23 | ### Lints and fixes files 24 | ``` 25 | npm run lint 26 | ``` 27 | 28 | ### Customize configuration 29 | See [Configuration Reference](https://cli.vuejs.org/config/). 30 | -------------------------------------------------------------------------------- /demo/app/App.vue: -------------------------------------------------------------------------------- 1 | 12 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /demo/app/App_Resources/Android/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /demo/app/App_Resources/Android/app.gradle: -------------------------------------------------------------------------------- 1 | // Add your native dependencies here: 2 | 3 | android { 4 | defaultConfig { 5 | generatedDensities = [] 6 | applicationId = "org.nativescript.application" 7 | } 8 | aaptOptions { 9 | additionalParameters "--no-version-vectors" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /demo/app/App_Resources/Android/drawable-hdpi/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jawa-the-hutt/nativescript-vue-shadow/bbb5d17d0540c99de58590207337c72fd98e36f0/demo/app/App_Resources/Android/drawable-hdpi/background.png -------------------------------------------------------------------------------- /demo/app/App_Resources/Android/drawable-hdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jawa-the-hutt/nativescript-vue-shadow/bbb5d17d0540c99de58590207337c72fd98e36f0/demo/app/App_Resources/Android/drawable-hdpi/icon.png -------------------------------------------------------------------------------- /demo/app/App_Resources/Android/drawable-hdpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jawa-the-hutt/nativescript-vue-shadow/bbb5d17d0540c99de58590207337c72fd98e36f0/demo/app/App_Resources/Android/drawable-hdpi/logo.png -------------------------------------------------------------------------------- /demo/app/App_Resources/Android/drawable-ldpi/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jawa-the-hutt/nativescript-vue-shadow/bbb5d17d0540c99de58590207337c72fd98e36f0/demo/app/App_Resources/Android/drawable-ldpi/background.png -------------------------------------------------------------------------------- /demo/app/App_Resources/Android/drawable-ldpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jawa-the-hutt/nativescript-vue-shadow/bbb5d17d0540c99de58590207337c72fd98e36f0/demo/app/App_Resources/Android/drawable-ldpi/icon.png -------------------------------------------------------------------------------- /demo/app/App_Resources/Android/drawable-ldpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jawa-the-hutt/nativescript-vue-shadow/bbb5d17d0540c99de58590207337c72fd98e36f0/demo/app/App_Resources/Android/drawable-ldpi/logo.png -------------------------------------------------------------------------------- /demo/app/App_Resources/Android/drawable-mdpi/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jawa-the-hutt/nativescript-vue-shadow/bbb5d17d0540c99de58590207337c72fd98e36f0/demo/app/App_Resources/Android/drawable-mdpi/background.png -------------------------------------------------------------------------------- /demo/app/App_Resources/Android/drawable-mdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jawa-the-hutt/nativescript-vue-shadow/bbb5d17d0540c99de58590207337c72fd98e36f0/demo/app/App_Resources/Android/drawable-mdpi/icon.png -------------------------------------------------------------------------------- /demo/app/App_Resources/Android/drawable-mdpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jawa-the-hutt/nativescript-vue-shadow/bbb5d17d0540c99de58590207337c72fd98e36f0/demo/app/App_Resources/Android/drawable-mdpi/logo.png -------------------------------------------------------------------------------- /demo/app/App_Resources/Android/drawable-nodpi/splash_screen.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /demo/app/App_Resources/Android/drawable-xhdpi/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jawa-the-hutt/nativescript-vue-shadow/bbb5d17d0540c99de58590207337c72fd98e36f0/demo/app/App_Resources/Android/drawable-xhdpi/background.png -------------------------------------------------------------------------------- /demo/app/App_Resources/Android/drawable-xhdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jawa-the-hutt/nativescript-vue-shadow/bbb5d17d0540c99de58590207337c72fd98e36f0/demo/app/App_Resources/Android/drawable-xhdpi/icon.png -------------------------------------------------------------------------------- /demo/app/App_Resources/Android/drawable-xhdpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jawa-the-hutt/nativescript-vue-shadow/bbb5d17d0540c99de58590207337c72fd98e36f0/demo/app/App_Resources/Android/drawable-xhdpi/logo.png -------------------------------------------------------------------------------- /demo/app/App_Resources/Android/drawable-xxhdpi/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jawa-the-hutt/nativescript-vue-shadow/bbb5d17d0540c99de58590207337c72fd98e36f0/demo/app/App_Resources/Android/drawable-xxhdpi/background.png -------------------------------------------------------------------------------- /demo/app/App_Resources/Android/drawable-xxhdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jawa-the-hutt/nativescript-vue-shadow/bbb5d17d0540c99de58590207337c72fd98e36f0/demo/app/App_Resources/Android/drawable-xxhdpi/icon.png -------------------------------------------------------------------------------- /demo/app/App_Resources/Android/drawable-xxhdpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jawa-the-hutt/nativescript-vue-shadow/bbb5d17d0540c99de58590207337c72fd98e36f0/demo/app/App_Resources/Android/drawable-xxhdpi/logo.png -------------------------------------------------------------------------------- /demo/app/App_Resources/Android/drawable-xxxhdpi/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jawa-the-hutt/nativescript-vue-shadow/bbb5d17d0540c99de58590207337c72fd98e36f0/demo/app/App_Resources/Android/drawable-xxxhdpi/background.png -------------------------------------------------------------------------------- /demo/app/App_Resources/Android/drawable-xxxhdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jawa-the-hutt/nativescript-vue-shadow/bbb5d17d0540c99de58590207337c72fd98e36f0/demo/app/App_Resources/Android/drawable-xxxhdpi/icon.png -------------------------------------------------------------------------------- /demo/app/App_Resources/Android/drawable-xxxhdpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jawa-the-hutt/nativescript-vue-shadow/bbb5d17d0540c99de58590207337c72fd98e36f0/demo/app/App_Resources/Android/drawable-xxxhdpi/logo.png -------------------------------------------------------------------------------- /demo/app/App_Resources/Android/values-v21/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3d5afe 4 | 5 | -------------------------------------------------------------------------------- /demo/app/App_Resources/Android/values-v21/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | demo 4 | demo 5 | 6 | -------------------------------------------------------------------------------- /demo/app/App_Resources/Android/values-v21/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 11 | 14 | 15 | 16 | 19 | 20 | 23 | 24 | -------------------------------------------------------------------------------- /demo/app/App_Resources/Android/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #F5F5F5 4 | #757575 5 | #33B5E5 6 | #272734 7 | 8 | -------------------------------------------------------------------------------- /demo/app/App_Resources/Android/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | demo 4 | demo 5 | 6 | -------------------------------------------------------------------------------- /demo/app/App_Resources/Android/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 17 | 18 | 20 | 21 | 22 | 29 | 30 | 32 | 33 | 34 | 39 | 40 | 42 | 43 | -------------------------------------------------------------------------------- /demo/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "29x29", 5 | "idiom" : "iphone", 6 | "filename" : "icon-29.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "29x29", 11 | "idiom" : "iphone", 12 | "filename" : "icon-29@2x.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "icon-29@3x.png", 19 | "scale" : "3x" 20 | }, 21 | { 22 | "size" : "40x40", 23 | "idiom" : "iphone", 24 | "filename" : "icon-40@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "40x40", 29 | "idiom" : "iphone", 30 | "filename" : "icon-40@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "60x60", 35 | "idiom" : "iphone", 36 | "filename" : "icon-60@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "60x60", 41 | "idiom" : "iphone", 42 | "filename" : "icon-60@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "29x29", 47 | "idiom" : "ipad", 48 | "filename" : "icon-29.png", 49 | "scale" : "1x" 50 | }, 51 | { 52 | "size" : "29x29", 53 | "idiom" : "ipad", 54 | "filename" : "icon-29@2x.png", 55 | "scale" : "2x" 56 | }, 57 | { 58 | "size" : "40x40", 59 | "idiom" : "ipad", 60 | "filename" : "icon-40.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "40x40", 65 | "idiom" : "ipad", 66 | "filename" : "icon-40@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "76x76", 71 | "idiom" : "ipad", 72 | "filename" : "icon-76.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "76x76", 77 | "idiom" : "ipad", 78 | "filename" : "icon-76@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "83.5x83.5", 83 | "idiom" : "ipad", 84 | "filename" : "icon-83.5@2x.png", 85 | "scale" : "2x" 86 | }, 87 | { 88 | "size" : "1024x1024", 89 | "idiom" : "ios-marketing", 90 | "filename" : "icon-1024.png", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /demo/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jawa-the-hutt/nativescript-vue-shadow/bbb5d17d0540c99de58590207337c72fd98e36f0/demo/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-1024.png -------------------------------------------------------------------------------- /demo/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jawa-the-hutt/nativescript-vue-shadow/bbb5d17d0540c99de58590207337c72fd98e36f0/demo/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29.png -------------------------------------------------------------------------------- /demo/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jawa-the-hutt/nativescript-vue-shadow/bbb5d17d0540c99de58590207337c72fd98e36f0/demo/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png -------------------------------------------------------------------------------- /demo/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jawa-the-hutt/nativescript-vue-shadow/bbb5d17d0540c99de58590207337c72fd98e36f0/demo/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png -------------------------------------------------------------------------------- /demo/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jawa-the-hutt/nativescript-vue-shadow/bbb5d17d0540c99de58590207337c72fd98e36f0/demo/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40.png -------------------------------------------------------------------------------- /demo/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jawa-the-hutt/nativescript-vue-shadow/bbb5d17d0540c99de58590207337c72fd98e36f0/demo/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png -------------------------------------------------------------------------------- /demo/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jawa-the-hutt/nativescript-vue-shadow/bbb5d17d0540c99de58590207337c72fd98e36f0/demo/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png -------------------------------------------------------------------------------- /demo/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jawa-the-hutt/nativescript-vue-shadow/bbb5d17d0540c99de58590207337c72fd98e36f0/demo/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png -------------------------------------------------------------------------------- /demo/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jawa-the-hutt/nativescript-vue-shadow/bbb5d17d0540c99de58590207337c72fd98e36f0/demo/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png -------------------------------------------------------------------------------- /demo/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jawa-the-hutt/nativescript-vue-shadow/bbb5d17d0540c99de58590207337c72fd98e36f0/demo/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76.png -------------------------------------------------------------------------------- /demo/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jawa-the-hutt/nativescript-vue-shadow/bbb5d17d0540c99de58590207337c72fd98e36f0/demo/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png -------------------------------------------------------------------------------- /demo/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jawa-the-hutt/nativescript-vue-shadow/bbb5d17d0540c99de58590207337c72fd98e36f0/demo/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png -------------------------------------------------------------------------------- /demo/app/App_Resources/iOS/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /demo/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "extent" : "full-screen", 5 | "idiom" : "iphone", 6 | "subtype" : "2436h", 7 | "filename" : "Default-1125h.png", 8 | "minimum-system-version" : "11.0", 9 | "orientation" : "portrait", 10 | "scale" : "3x" 11 | }, 12 | { 13 | "orientation" : "landscape", 14 | "idiom" : "iphone", 15 | "extent" : "full-screen", 16 | "filename" : "Default-Landscape-X.png", 17 | "minimum-system-version" : "11.0", 18 | "subtype" : "2436h", 19 | "scale" : "3x" 20 | }, 21 | { 22 | "extent" : "full-screen", 23 | "idiom" : "iphone", 24 | "subtype" : "736h", 25 | "filename" : "Default-736h@3x.png", 26 | "minimum-system-version" : "8.0", 27 | "orientation" : "portrait", 28 | "scale" : "3x" 29 | }, 30 | { 31 | "extent" : "full-screen", 32 | "idiom" : "iphone", 33 | "subtype" : "736h", 34 | "filename" : "Default-Landscape@3x.png", 35 | "minimum-system-version" : "8.0", 36 | "orientation" : "landscape", 37 | "scale" : "3x" 38 | }, 39 | { 40 | "extent" : "full-screen", 41 | "idiom" : "iphone", 42 | "subtype" : "667h", 43 | "filename" : "Default-667h@2x.png", 44 | "minimum-system-version" : "8.0", 45 | "orientation" : "portrait", 46 | "scale" : "2x" 47 | }, 48 | { 49 | "orientation" : "portrait", 50 | "idiom" : "iphone", 51 | "filename" : "Default@2x.png", 52 | "extent" : "full-screen", 53 | "minimum-system-version" : "7.0", 54 | "scale" : "2x" 55 | }, 56 | { 57 | "extent" : "full-screen", 58 | "idiom" : "iphone", 59 | "subtype" : "retina4", 60 | "filename" : "Default-568h@2x.png", 61 | "minimum-system-version" : "7.0", 62 | "orientation" : "portrait", 63 | "scale" : "2x" 64 | }, 65 | { 66 | "orientation" : "portrait", 67 | "idiom" : "ipad", 68 | "filename" : "Default-Portrait.png", 69 | "extent" : "full-screen", 70 | "minimum-system-version" : "7.0", 71 | "scale" : "1x" 72 | }, 73 | { 74 | "orientation" : "landscape", 75 | "idiom" : "ipad", 76 | "filename" : "Default-Landscape.png", 77 | "extent" : "full-screen", 78 | "minimum-system-version" : "7.0", 79 | "scale" : "1x" 80 | }, 81 | { 82 | "orientation" : "portrait", 83 | "idiom" : "ipad", 84 | "filename" : "Default-Portrait@2x.png", 85 | "extent" : "full-screen", 86 | "minimum-system-version" : "7.0", 87 | "scale" : "2x" 88 | }, 89 | { 90 | "orientation" : "landscape", 91 | "idiom" : "ipad", 92 | "filename" : "Default-Landscape@2x.png", 93 | "extent" : "full-screen", 94 | "minimum-system-version" : "7.0", 95 | "scale" : "2x" 96 | }, 97 | { 98 | "orientation" : "portrait", 99 | "idiom" : "iphone", 100 | "filename" : "Default.png", 101 | "extent" : "full-screen", 102 | "scale" : "1x" 103 | }, 104 | { 105 | "orientation" : "portrait", 106 | "idiom" : "iphone", 107 | "filename" : "Default@2x.png", 108 | "extent" : "full-screen", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "orientation" : "portrait", 113 | "idiom" : "iphone", 114 | "filename" : "Default-568h@2x.png", 115 | "extent" : "full-screen", 116 | "subtype" : "retina4", 117 | "scale" : "2x" 118 | }, 119 | { 120 | "orientation" : "portrait", 121 | "idiom" : "ipad", 122 | "extent" : "to-status-bar", 123 | "scale" : "1x" 124 | }, 125 | { 126 | "orientation" : "portrait", 127 | "idiom" : "ipad", 128 | "filename" : "Default-Portrait.png", 129 | "extent" : "full-screen", 130 | "scale" : "1x" 131 | }, 132 | { 133 | "orientation" : "landscape", 134 | "idiom" : "ipad", 135 | "extent" : "to-status-bar", 136 | "scale" : "1x" 137 | }, 138 | { 139 | "orientation" : "landscape", 140 | "idiom" : "ipad", 141 | "filename" : "Default-Landscape.png", 142 | "extent" : "full-screen", 143 | "scale" : "1x" 144 | }, 145 | { 146 | "orientation" : "portrait", 147 | "idiom" : "ipad", 148 | "extent" : "to-status-bar", 149 | "scale" : "2x" 150 | }, 151 | { 152 | "orientation" : "portrait", 153 | "idiom" : "ipad", 154 | "filename" : "Default-Portrait@2x.png", 155 | "extent" : "full-screen", 156 | "scale" : "2x" 157 | }, 158 | { 159 | "orientation" : "landscape", 160 | "idiom" : "ipad", 161 | "extent" : "to-status-bar", 162 | "scale" : "2x" 163 | }, 164 | { 165 | "orientation" : "landscape", 166 | "idiom" : "ipad", 167 | "filename" : "Default-Landscape@2x.png", 168 | "extent" : "full-screen", 169 | "scale" : "2x" 170 | } 171 | ], 172 | "info" : { 173 | "version" : 1, 174 | "author" : "xcode" 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /demo/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-1125h.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jawa-the-hutt/nativescript-vue-shadow/bbb5d17d0540c99de58590207337c72fd98e36f0/demo/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-1125h.png -------------------------------------------------------------------------------- /demo/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-568h@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jawa-the-hutt/nativescript-vue-shadow/bbb5d17d0540c99de58590207337c72fd98e36f0/demo/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-568h@2x.png -------------------------------------------------------------------------------- /demo/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-667h@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jawa-the-hutt/nativescript-vue-shadow/bbb5d17d0540c99de58590207337c72fd98e36f0/demo/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-667h@2x.png -------------------------------------------------------------------------------- /demo/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-736h@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jawa-the-hutt/nativescript-vue-shadow/bbb5d17d0540c99de58590207337c72fd98e36f0/demo/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-736h@3x.png -------------------------------------------------------------------------------- /demo/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape-X.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jawa-the-hutt/nativescript-vue-shadow/bbb5d17d0540c99de58590207337c72fd98e36f0/demo/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape-X.png -------------------------------------------------------------------------------- /demo/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jawa-the-hutt/nativescript-vue-shadow/bbb5d17d0540c99de58590207337c72fd98e36f0/demo/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape.png -------------------------------------------------------------------------------- /demo/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jawa-the-hutt/nativescript-vue-shadow/bbb5d17d0540c99de58590207337c72fd98e36f0/demo/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@2x.png -------------------------------------------------------------------------------- /demo/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jawa-the-hutt/nativescript-vue-shadow/bbb5d17d0540c99de58590207337c72fd98e36f0/demo/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@3x.png -------------------------------------------------------------------------------- /demo/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jawa-the-hutt/nativescript-vue-shadow/bbb5d17d0540c99de58590207337c72fd98e36f0/demo/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait.png -------------------------------------------------------------------------------- /demo/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jawa-the-hutt/nativescript-vue-shadow/bbb5d17d0540c99de58590207337c72fd98e36f0/demo/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait@2x.png -------------------------------------------------------------------------------- /demo/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jawa-the-hutt/nativescript-vue-shadow/bbb5d17d0540c99de58590207337c72fd98e36f0/demo/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default.png -------------------------------------------------------------------------------- /demo/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jawa-the-hutt/nativescript-vue-shadow/bbb5d17d0540c99de58590207337c72fd98e36f0/demo/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default@2x.png -------------------------------------------------------------------------------- /demo/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchScreen-AspectFill.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchScreen-AspectFill@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /demo/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jawa-the-hutt/nativescript-vue-shadow/bbb5d17d0540c99de58590207337c72fd98e36f0/demo/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill.png -------------------------------------------------------------------------------- /demo/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jawa-the-hutt/nativescript-vue-shadow/bbb5d17d0540c99de58590207337c72fd98e36f0/demo/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill@2x.png -------------------------------------------------------------------------------- /demo/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchScreen-Center.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchScreen-Center@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /demo/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jawa-the-hutt/nativescript-vue-shadow/bbb5d17d0540c99de58590207337c72fd98e36f0/demo/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center.png -------------------------------------------------------------------------------- /demo/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jawa-the-hutt/nativescript-vue-shadow/bbb5d17d0540c99de58590207337c72fd98e36f0/demo/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center@2x.png -------------------------------------------------------------------------------- /demo/app/App_Resources/iOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | demo 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 0.1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 0.1.0 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIRequiresFullScreen 28 | 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /demo/app/App_Resources/iOS/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /demo/app/App_Resources/iOS/build.xcconfig: -------------------------------------------------------------------------------- 1 | // You can add custom settings here 2 | // for example you can uncomment the following line to force distribution code signing 3 | // CODE_SIGN_IDENTITY = iPhone Distribution 4 | // To build for device with Xcode 8 you need to specify your development team. More info: https://developer.apple.com/library/prerelease/content/releasenotes/DeveloperTools/RN-Xcode/Introduction.html 5 | // DEVELOPMENT_TEAM = YOUR_TEAM_ID; 6 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 7 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 8 | -------------------------------------------------------------------------------- /demo/app/assets/demo-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jawa-the-hutt/nativescript-vue-shadow/bbb5d17d0540c99de58590207337c72fd98e36f0/demo/app/assets/demo-screenshot.png -------------------------------------------------------------------------------- /demo/app/main.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'nativescript-vue'; 2 | import App from './App.vue'; 3 | 4 | import NSVueShadow from '../../' 5 | Vue.use(NSVueShadow) 6 | 7 | // Set the following to `true` to hide the logs created by nativescript-vue 8 | Vue.config.silent = false; 9 | // Set the following to `false` to not colorize the logs created by nativescript-vue 10 | // disabled in template due to typing issue for Typescript projects....NEEDS TO BE FIXED 11 | // @ts-ignore 12 | Vue.config.debug = true; 13 | 14 | // setup NS-Vue Devtools for use 15 | import VueDevtools from 'nativescript-vue-devtools'; 16 | Vue.use(VueDevtools, { host: '10.0.2.2' }); 17 | 18 | new Vue({ 19 | render: h => h('frame', [h(App)]), 20 | }).$start(); 21 | -------------------------------------------------------------------------------- /demo/app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "android": { 3 | "v8Flags": "--expose_gc", 4 | "markingMode": "none" 5 | }, 6 | "main": "main", 7 | "name": "demo", 8 | "version": "0.1.0" 9 | } 10 | -------------------------------------------------------------------------------- /demo/app/views/Home.vue: -------------------------------------------------------------------------------- 1 | 25 | 99 | 100 | 133 | -------------------------------------------------------------------------------- /demo/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [ 3 | '@babel/plugin-syntax-dynamic-import' 4 | ], 5 | presets: [ 6 | process.env.VUE_PLATFORM === 'web' ? '@vue/app' : {}, 7 | ['@babel/env', { targets: { esmodules: true } }] 8 | ] 9 | } -------------------------------------------------------------------------------- /demo/nsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "appPath": "app", 3 | "appResourcesPath": "app/App_Resources" 4 | } -------------------------------------------------------------------------------- /demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "demo", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "lint": "vue-cli-service lint", 7 | "build:android": "npm run setup-webpack-config && tns build android --bundle --env.production && npm run remove-webpack-config", 8 | "build:ios": "npm run setup-webpack-config && tns build ios --bundle --env.production && npm run remove-webpack-config", 9 | "build:web": "vue-cli-service build --mode production.web", 10 | "clean:android": "rimraf platforms/android", 11 | "clean:ios": "rimraf platforms/ios", 12 | "clean:platforms": "rimraf platforms", 13 | "debug:android": "npm run setup-webpack-config && tns debug android --bundle --env.development", 14 | "debug:ios": "npm run setup-webpack-config && tns debug ios --bundle --env.development", 15 | "preview:android": "npm run setup-webpack-config && tns preview --bundle --env.development --env.android", 16 | "preview:ios": "npm run setup-webpack-config && tns preview --bundle --env.development --env.ios", 17 | "remove-webpack-config": "node ./node_modules/vue-cli-plugin-nativescript-vue/lib/scripts/webpack-maintenance post", 18 | "serve:android": "npm run setup-webpack-config && tns run android --bundle --env.development", 19 | "serve:ios": "npm run setup-webpack-config && tns run ios --bundle --env.development", 20 | "serve:web": "vue-cli-service serve --mode development.web", 21 | "setup-webpack-config": "node ./node_modules/vue-cli-plugin-nativescript-vue/lib/scripts/webpack-maintenance pre" 22 | }, 23 | "dependencies": { 24 | "@vue/devtools": "^5.0.9", 25 | "core-js": "^3.0.1", 26 | "nativescript-socketio": "^3.2.1", 27 | "nativescript-toasty": "^1.3.0", 28 | "nativescript-vue": "^2.2.2", 29 | "nativescript-vue-devtools": "^1.2.0", 30 | "tns-core-modules": "^5.3.1", 31 | "vue": "^2.6.10", 32 | "vue-class-component": "^7.0.2", 33 | "vue-property-decorator": "^8.1.0" 34 | }, 35 | "devDependencies": { 36 | "@babel/core": "^7.4.0", 37 | "@babel/preset-env": "^7.4.1", 38 | "@babel/traverse": "^7.4.0", 39 | "@babel/types": "^7.4.0", 40 | "@vue/cli-plugin-babel": "^3.6.0", 41 | "@vue/cli-plugin-eslint": "^3.6.0", 42 | "@vue/cli-plugin-typescript": "^3.6.0", 43 | "@vue/cli-service": "^3.6.0", 44 | "@vue/eslint-config-prettier": "^4.0.1", 45 | "@vue/eslint-config-typescript": "^4.0.0", 46 | "babel-eslint": "^10.0.1", 47 | "babel-loader": "^8.0.5", 48 | "eslint": "^5.16.0", 49 | "eslint-plugin-vue": "^5.0.0", 50 | "fork-ts-checker-webpack-plugin": "^1.0.0", 51 | "nativescript-dev-webpack": "^0.21.2", 52 | "nativescript-vue-template-compiler": "^2.2.2", 53 | "nativescript-worker-loader": "~0.9.5", 54 | "node-sass": "^4.11.0", 55 | "rimraf": "^2.6.3", 56 | "sass-loader": "^7.1.0", 57 | "string-replace-loader": "^2.1.1", 58 | "terser-webpack-plugin": "^1.2.3", 59 | "typescript": "^3.4.3", 60 | "vue-cli-plugin-nativescript-vue": "0.0.14", 61 | "vue-template-compiler": "^2.6.10", 62 | "webpack": "^4.29.6", 63 | "webpack-cli": "^3.3.0" 64 | }, 65 | "nativescript": { 66 | "id": "org.nativescript.application", 67 | "tns-ios": { 68 | "version": "5.3.0" 69 | }, 70 | "tns-android": { 71 | "version": "5.3.0" 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /demo/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | autoprefixer: {} 4 | } 5 | }; 6 | -------------------------------------------------------------------------------- /demo/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jawa-the-hutt/nativescript-vue-shadow/bbb5d17d0540c99de58590207337c72fd98e36f0/demo/public/favicon.ico -------------------------------------------------------------------------------- /demo/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | demo 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /demo/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "strict": true, 6 | "jsx": "preserve", 7 | "importHelpers": true, 8 | "moduleResolution": "node", 9 | "experimentalDecorators": true, 10 | "esModuleInterop": true, 11 | "allowSyntheticDefaultImports": true, 12 | "sourceMap": true, 13 | "baseUrl": ".", 14 | "types": [ 15 | "node" 16 | ], 17 | "paths": { 18 | "@/*": [ 19 | "app/*" 20 | ], 21 | "assets/*": [ 22 | "app/assets/*" 23 | ], 24 | "fonts/*": [ 25 | "app/fonts/*" 26 | ], 27 | "components/*": [ 28 | "app/components/*" 29 | ], 30 | "styles/*": [ 31 | "app/styles/*" 32 | ], 33 | "app/*": [ 34 | "app/*" 35 | ] 36 | }, 37 | "lib": [ 38 | "esnext", 39 | "dom", 40 | "dom.iterable", 41 | "scripthost" 42 | ], 43 | "noImplicitAny": false 44 | }, 45 | "include": [ 46 | "app/**/*.ts", 47 | "app/**/*.tsx", 48 | "app/**/*.vue", 49 | "tests/**/*.ts", 50 | "tests/**/*.tsx", 51 | "types/**/*.d.ts" 52 | ], 53 | "exclude": [ 54 | "node_modules" 55 | ] 56 | } 57 | -------------------------------------------------------------------------------- /demo/types/globals.d.ts: -------------------------------------------------------------------------------- 1 | declare const TNS_ENV: string; 2 | declare const TNS_APP_PLATFORM: string; 3 | declare const TNS_APP_MODE: string; -------------------------------------------------------------------------------- /demo/types/shims-tsx.d.ts: -------------------------------------------------------------------------------- 1 | import Vue, { VNode } from 'vue'; 2 | 3 | declare global { 4 | namespace JSX { 5 | // tslint:disable no-empty-interface 6 | interface Element extends VNode {} 7 | // tslint:disable no-empty-interface 8 | interface ElementClass extends Vue {} 9 | interface IntrinsicElements { 10 | [elem: string]: any; 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /demo/types/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.vue' { 2 | import Vue from 'vue'; 3 | export default Vue; 4 | } 5 | -------------------------------------------------------------------------------- /demo/vue.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | chainWebpack: (config) => { 3 | // for debugging the project 4 | config.devtool('inline-source-map'); 5 | }, 6 | runtimeCompiler: true 7 | }; 8 | -------------------------------------------------------------------------------- /demo/webpack.config.js: -------------------------------------------------------------------------------- 1 | // this file is for cases where we need to access the 2 | // webpack config as a file when using CLI commands. 3 | 4 | let service = process.VUE_CLI_SERVICE 5 | 6 | if (!service || process.env.VUE_CLI_API_MODE) { 7 | const Service = require('@vue/cli-service/lib/Service') 8 | service = new Service(process.env.VUE_CLI_CONTEXT || process.cwd()) 9 | service.init(process.env.VUE_CLI_MODE || process.env.NODE_ENV) 10 | } 11 | 12 | module.exports = service.resolveWebpackConfig() 13 | -------------------------------------------------------------------------------- /dist/nativescript-vue-shadow.esm.js: -------------------------------------------------------------------------------- 1 | import Vue from 'nativescript-vue'; 2 | import { isAndroid, screen, isIOS } from 'tns-core-modules/platform'; 3 | import { Color } from 'tns-core-modules/color'; 4 | import { Length, View } from 'tns-core-modules/ui/page/page'; 5 | import { addWeakEventListener, removeWeakEventListener } from 'tns-core-modules/ui/core/weak-event-listener'; 6 | 7 | var ShapeEnum; 8 | (function (ShapeEnum) { 9 | ShapeEnum["RECTANGLE"] = "RECTANGLE"; 10 | ShapeEnum["OVAL"] = "OVAL"; 11 | ShapeEnum["RING"] = "RING"; 12 | ShapeEnum["LINE"] = "LINE"; 13 | })(ShapeEnum || (ShapeEnum = {})); 14 | 15 | let LayeredShadow; 16 | let PlainShadow; 17 | if (isAndroid) { 18 | LayeredShadow = android.graphics.drawable.LayerDrawable.extend({}); 19 | PlainShadow = android.graphics.drawable.GradientDrawable.extend({}); 20 | } 21 | const classCache = {}; 22 | function getAndroidR(rtype, field) { 23 | const className = "android.R$" + rtype; 24 | if (!classCache.hasOwnProperty(className)) { 25 | classCache[className] = { 26 | class: java.lang.Class.forName(className), 27 | fieldCache: {} 28 | }; 29 | } 30 | if (!classCache[className].fieldCache.hasOwnProperty(field)) { 31 | classCache[className].fieldCache[field] = +classCache[className].class.getField(field).get(null); 32 | } 33 | return classCache[className].fieldCache[field]; 34 | } 35 | class Shadow { 36 | static apply(tnsView, data) { 37 | const LOLLIPOP = 21; 38 | if (tnsView.android && 39 | android.os.Build.VERSION.SDK_INT >= LOLLIPOP) { 40 | Shadow.applyOnAndroid(tnsView, Shadow.getDefaults(data)); 41 | } 42 | else if (tnsView.ios) { 43 | Shadow.applyOnIOS(tnsView, Shadow.getDefaults(data)); 44 | } 45 | } 46 | static getDefaults(data) { 47 | return Object.assign({}, data, { 48 | shape: data.shape || Shadow.DEFAULT_SHAPE, 49 | pressedElevation: data.pressedElevation || Shadow.DEFAULT_PRESSED_ELEVATION, 50 | pressedTranslationZ: data.pressedTranslationZ || Shadow.DEFAULT_PRESSED_ELEVATION, 51 | shadowColor: data.shadowColor || 52 | Shadow.DEFAULT_SHADOW_COLOR, 53 | useShadowPath: (data.useShadowPath !== undefined ? data.useShadowPath : true), 54 | rasterize: (data.rasterize !== undefined ? data.rasterize : false) 55 | }); 56 | } 57 | static isShadow(drawable) { 58 | return (drawable instanceof LayeredShadow || drawable instanceof PlainShadow); 59 | } 60 | static applyOnAndroid(tnsView, data) { 61 | const nativeView = tnsView.android; 62 | let shape; 63 | let overrideBackground = true; 64 | let currentBg = nativeView.getBackground(); 65 | if (currentBg instanceof android.graphics.drawable.RippleDrawable) { 66 | let rippleBg = currentBg.getDrawable(0); 67 | if (rippleBg instanceof android.graphics.drawable.InsetDrawable) { 68 | overrideBackground = false; 69 | } 70 | else if (Shadow.isShadow(rippleBg)) { 71 | currentBg = rippleBg; 72 | } 73 | } 74 | if (overrideBackground) { 75 | if (Shadow.isShadow(currentBg)) { 76 | currentBg = currentBg instanceof LayeredShadow ? 77 | currentBg.getDrawable(1) : null; 78 | } 79 | const outerRadii = Array.create("float", 8); 80 | if (data.cornerRadius === undefined) { 81 | outerRadii[0] = outerRadii[1] = Length.toDevicePixels(tnsView.borderTopLeftRadius, 0); 82 | outerRadii[2] = outerRadii[3] = Length.toDevicePixels(tnsView.borderTopRightRadius, 0); 83 | outerRadii[4] = outerRadii[5] = Length.toDevicePixels(tnsView.borderBottomRightRadius, 0); 84 | outerRadii[6] = outerRadii[7] = Length.toDevicePixels(tnsView.borderBottomLeftRadius, 0); 85 | } 86 | else { 87 | java.util.Arrays.fill(outerRadii, Shadow.androidDipToPx(nativeView, data.cornerRadius)); 88 | } 89 | const bgColor = currentBg ? 90 | (currentBg instanceof android.graphics.drawable.ColorDrawable && currentBg.getColor() ? 91 | currentBg.getColor() : android.graphics.Color.parseColor(data.bgcolor || Shadow.DEFAULT_BGCOLOR)) : 92 | android.graphics.Color.parseColor(data.bgcolor || Shadow.DEFAULT_BGCOLOR); 93 | let newBg; 94 | if (data.shape !== ShapeEnum.RECTANGLE || data.bgcolor || !currentBg) { 95 | shape = new PlainShadow(); 96 | shape.setShape(android.graphics.drawable.GradientDrawable[data.shape]); 97 | shape.setCornerRadii(outerRadii); 98 | shape.setColor(bgColor); 99 | newBg = shape; 100 | } 101 | else { 102 | const r = new android.graphics.drawable.shapes.RoundRectShape(outerRadii, null, null); 103 | shape = new android.graphics.drawable.ShapeDrawable(r); 104 | shape.getPaint().setColor(bgColor); 105 | var arr = Array.create(android.graphics.drawable.Drawable, 2); 106 | arr[0] = shape; 107 | arr[1] = currentBg; 108 | const drawable = new LayeredShadow(arr); 109 | newBg = drawable; 110 | } 111 | nativeView.setBackgroundDrawable(newBg); 112 | } 113 | nativeView.setElevation(Shadow.androidDipToPx(nativeView, data.elevation)); 114 | nativeView.setTranslationZ(Shadow.androidDipToPx(nativeView, data.translationZ)); 115 | if (nativeView.getStateListAnimator() || data.forcePressAnimation) { 116 | this.overrideDefaultAnimator(nativeView, data); 117 | } 118 | } 119 | static overrideDefaultAnimator(nativeView, data) { 120 | const sla = new android.animation.StateListAnimator(); 121 | const ObjectAnimator = android.animation.ObjectAnimator; 122 | const AnimatorSet = android.animation.AnimatorSet; 123 | const shortAnimTime = getAndroidR("integer", "config_shortAnimTime"); 124 | const buttonDuration = nativeView.getContext().getResources().getInteger(shortAnimTime) / 2; 125 | const pressedElevation = this.androidDipToPx(nativeView, data.pressedElevation); 126 | const pressedZ = this.androidDipToPx(nativeView, data.pressedTranslationZ); 127 | const elevation = this.androidDipToPx(nativeView, data.elevation); 128 | const z = this.androidDipToPx(nativeView, data.translationZ || 0); 129 | const pressedSet = new AnimatorSet(); 130 | const notPressedSet = new AnimatorSet(); 131 | const defaultSet = new AnimatorSet(); 132 | pressedSet.playTogether(java.util.Arrays.asList([ 133 | ObjectAnimator.ofFloat(nativeView, "translationZ", [pressedZ]) 134 | .setDuration(buttonDuration), 135 | ObjectAnimator.ofFloat(nativeView, "elevation", [pressedElevation]) 136 | .setDuration(0), 137 | ])); 138 | notPressedSet.playTogether(java.util.Arrays.asList([ 139 | ObjectAnimator.ofFloat(nativeView, "translationZ", [z]) 140 | .setDuration(buttonDuration), 141 | ObjectAnimator.ofFloat(nativeView, "elevation", [elevation]) 142 | .setDuration(0), 143 | ])); 144 | defaultSet.playTogether(java.util.Arrays.asList([ 145 | ObjectAnimator.ofFloat(nativeView, "translationZ", [0]).setDuration(0), 146 | ObjectAnimator.ofFloat(nativeView, "elevation", [0]).setDuration(0), 147 | ])); 148 | sla.addState([getAndroidR("attr", "state_pressed"), getAndroidR("attr", "state_enabled")], pressedSet); 149 | sla.addState([getAndroidR("attr", "state_enabled")], notPressedSet); 150 | sla.addState([], defaultSet); 151 | nativeView.setStateListAnimator(sla); 152 | } 153 | static applyOnIOS(tnsView, data) { 154 | const nativeView = tnsView.ios; 155 | const elevation = parseFloat((data.elevation - 0).toFixed(2)); 156 | nativeView.layer.maskToBounds = false; 157 | nativeView.layer.shadowColor = new Color(data.shadowColor).ios.CGColor; 158 | nativeView.layer.shadowOffset = 159 | data.shadowOffset ? 160 | CGSizeMake(0, parseFloat(String(data.shadowOffset))) : 161 | CGSizeMake(0, 0.54 * elevation - 0.14); 162 | nativeView.layer.shadowOpacity = 163 | data.shadowOpacity ? 164 | parseFloat(String(data.shadowOpacity)) : 165 | 0.006 * elevation + 0.25; 166 | nativeView.layer.shadowRadius = 167 | data.shadowRadius ? 168 | parseFloat(String(data.shadowRadius)) : 169 | 0.66 * elevation - 0.5; 170 | nativeView.layer.shouldRasterize = data.rasterize; 171 | nativeView.layer.rasterizationScale = screen.mainScreen.scale; 172 | let shadowPath = null; 173 | if (data.useShadowPath) { 174 | shadowPath = UIBezierPath.bezierPathWithRoundedRectCornerRadius(nativeView.bounds, nativeView.layer.shadowRadius).cgPath; 175 | } 176 | nativeView.layer.shadowPath = shadowPath; 177 | } 178 | static androidDipToPx(nativeView, dip) { 179 | const metrics = nativeView.getContext().getResources().getDisplayMetrics(); 180 | return android.util.TypedValue.applyDimension(android.util.TypedValue.COMPLEX_UNIT_DIP, dip, metrics); 181 | } 182 | } 183 | Shadow.DEFAULT_SHAPE = ShapeEnum.RECTANGLE; 184 | Shadow.DEFAULT_BGCOLOR = '#FFFFFF'; 185 | Shadow.DEFAULT_SHADOW_COLOR = '#000000'; 186 | Shadow.DEFAULT_PRESSED_ELEVATION = 2; 187 | Shadow.DEFAULT_PRESSED_Z = 4; 188 | 189 | class NativeShadowDirective { 190 | constructor(el, binding) { 191 | this.loaded = false; 192 | this.initialized = false; 193 | this.eventsBound = false; 194 | this.monkeyPatch = (val) => { 195 | this.previousNSFn.call(this.el._nativeView, val); 196 | this.applyShadow(); 197 | }; 198 | this.el = el; 199 | if (binding.value && typeof binding.value !== 'object' && (typeof binding.value === 'string' || typeof binding.value === 'number')) { 200 | this.shadow = binding.value; 201 | this.elevation = binding.value; 202 | } 203 | if (binding.value && typeof binding.value === 'object' && binding.value.elevation) { 204 | this.shadow = binding.value; 205 | this.elevation = this.shadow.elevation; 206 | if (isAndroid && (('pressedElevation' in this.shadow) || 207 | ('shape' in this.shadow) || 208 | ('bgcolor' in this.shadow) || 209 | ('cornerRadius' in this.shadow) || 210 | ('translationZ' in this.shadow) || 211 | ('pressedTranslationZ' in this.shadow) || 212 | ('forcePressAnimation' in this.shadow))) { 213 | this.pressedElevation = this.shadow.pressedElevation; 214 | this.shape = this.shadow.shape; 215 | this.bgcolor = this.shadow.bgcolor; 216 | this.cornerRadius = this.shadow.cornerRadius; 217 | this.translationZ = this.shadow.translationZ; 218 | this.pressedTranslationZ = this.shadow.pressedTranslationZ; 219 | this.forcePressAnimation = this.shadow.forcePressAnimation; 220 | } 221 | else if (isIOS && (('maskToBounds' in this.shadow) || 222 | ('shadowColor' in this.shadow) || 223 | ('shadowOffset' in this.shadow) || 224 | ('shadowOpacity' in this.shadow) || 225 | ('shadowRadius' in this.shadow) || 226 | ('useShadowPath' in this.shadow) || 227 | ('rasterize' in this.shadow))) { 228 | this.maskToBounds = this.shadow.maskToBounds; 229 | this.shadowColor = this.shadow.shadowColor; 230 | this.shadowOffset = this.shadow.shadowOffset; 231 | this.shadowOpacity = this.shadow.shadowOpacity; 232 | this.shadowRadius = this.shadow.shadowRadius; 233 | this.useShadowPath = this.shadow.useShadowPath; 234 | this.rasterize = this.shadow.rasterize; 235 | } 236 | } 237 | if (isAndroid) { 238 | if (this.el._nativeView._redrawNativeBackground) { 239 | this.originalNSFn = this.el._nativeView._redrawNativeBackground; 240 | } 241 | } 242 | } 243 | initializeCommonData() { 244 | const tShadow = typeof this.shadow; 245 | if ((tShadow === 'string' || tShadow === 'number') && !this.elevation) { 246 | this.elevation = this.shadow ? parseInt(this.shadow, 10) : 2; 247 | } 248 | const tElevation = typeof this.elevation; 249 | if (tElevation === 'string' || tElevation === 'number') { 250 | this.elevation = this.elevation ? parseInt(this.elevation, 10) : 2; 251 | } 252 | } 253 | initializeAndroidData() { 254 | if (typeof this.cornerRadius === 'string') { 255 | this.cornerRadius = parseInt(this.cornerRadius, 10); 256 | } 257 | if (typeof this.translationZ === 'string') { 258 | this.translationZ = parseInt(this.translationZ, 10); 259 | } 260 | } 261 | initializeIOSData() { 262 | if (typeof this.shadowOffset === 'string') { 263 | this.shadowOffset = parseFloat(this.shadowOffset); 264 | } 265 | if (typeof this.shadowOpacity === 'string') { 266 | this.shadowOpacity = parseFloat(this.shadowOpacity); 267 | } 268 | if (typeof this.shadowRadius === 'string') { 269 | this.shadowRadius = parseFloat(this.shadowRadius); 270 | } 271 | } 272 | bindEvents() { 273 | if (!this.eventsBound) { 274 | addWeakEventListener(this.el._nativeView, View.loadedEvent, this.load, this); 275 | addWeakEventListener(this.el._nativeView, View.unloadedEvent, this.unload, this); 276 | this.eventsBound = true; 277 | if (this.el._nativeView.isLoaded) { 278 | this.load(); 279 | } 280 | } 281 | } 282 | unbindEvents() { 283 | if (this.eventsBound) { 284 | removeWeakEventListener(this.el._nativeView, View.loadedEvent, this.load, this); 285 | removeWeakEventListener(this.el._nativeView, View.unloadedEvent, this.unload, this); 286 | this.eventsBound = false; 287 | } 288 | } 289 | load() { 290 | this.loaded = true; 291 | this.applyShadow(); 292 | if (isAndroid) { 293 | this.previousNSFn = this.el._nativeView._redrawNativeBackground; 294 | this.el._nativeView._redrawNativeBackground = this.monkeyPatch; 295 | } 296 | } 297 | unload() { 298 | this.loaded = false; 299 | if (isAndroid) { 300 | this.el._nativeView._redrawNativeBackground = this.originalNSFn; 301 | } 302 | } 303 | applyShadow() { 304 | if (!this.shadow && !this.elevation) { 305 | return; 306 | } 307 | if (isAndroid) { 308 | if (android.os.Build.VERSION.SDK_INT < 21) { 309 | return; 310 | } 311 | } 312 | const viewToApplyShadowTo = isIOS ? this.iosShadowWrapper : this.el._nativeView; 313 | if (viewToApplyShadowTo) { 314 | Shadow.apply(viewToApplyShadowTo, { 315 | elevation: this.elevation, 316 | pressedElevation: this.pressedElevation, 317 | shape: this.shape, 318 | bgcolor: this.bgcolor, 319 | cornerRadius: this.cornerRadius, 320 | translationZ: this.translationZ, 321 | pressedTranslationZ: this.pressedTranslationZ, 322 | forcePressAnimation: this.forcePressAnimation, 323 | maskToBounds: this.maskToBounds, 324 | shadowColor: this.shadowColor, 325 | shadowOffset: this.shadowOffset, 326 | shadowOpacity: this.shadowOpacity, 327 | shadowRadius: this.shadowRadius, 328 | rasterize: this.rasterize, 329 | useShadowPath: this.useShadowPath 330 | }); 331 | } 332 | } 333 | loadFromAndroidData(data) { 334 | this.elevation = data.elevation || this.elevation; 335 | this.shape = data.shape || this.shape; 336 | this.bgcolor = data.bgcolor || this.bgcolor; 337 | this.cornerRadius = data.cornerRadius || this.cornerRadius; 338 | this.translationZ = data.translationZ || this.translationZ; 339 | this.pressedTranslationZ = data.pressedTranslationZ || this.pressedTranslationZ; 340 | this.forcePressAnimation = data.forcePressAnimation || this.forcePressAnimation; 341 | } 342 | loadFromIOSData(data) { 343 | this.maskToBounds = data.maskToBounds || this.maskToBounds; 344 | this.shadowColor = data.shadowColor || this.shadowColor; 345 | this.shadowOffset = data.shadowOffset || this.shadowOffset; 346 | this.shadowOpacity = data.shadowOpacity || this.shadowOpacity; 347 | this.shadowRadius = data.shadowRadius || this.shadowRadius; 348 | this.rasterize = data.rasterize || this.rasterize; 349 | this.useShadowPath = data.useShadowPath || this.useShadowPath; 350 | } 351 | init() { 352 | if (!this.initialized) { 353 | this.initialized = true; 354 | this.initializeCommonData(); 355 | if (isAndroid) { 356 | this.initializeAndroidData(); 357 | } 358 | else if (isIOS) { 359 | this.initializeIOSData(); 360 | } 361 | if (this.shadow && this.shadow.elevation) { 362 | if (isAndroid) { 363 | this.loadFromAndroidData(this.shadow); 364 | } 365 | else if (isIOS) { 366 | this.loadFromIOSData(this.shadow); 367 | } 368 | } 369 | if (!this.shadow && this.elevation) { 370 | if (isAndroid) { 371 | this.loadFromAndroidData({ 372 | elevation: this.elevation, 373 | pressedElevation: this.pressedElevation, 374 | shape: this.shape, 375 | bgcolor: this.bgcolor, 376 | cornerRadius: this.cornerRadius, 377 | translationZ: this.translationZ, 378 | pressedTranslationZ: this.pressedTranslationZ, 379 | forcePressAnimation: this.forcePressAnimation, 380 | }); 381 | } 382 | else if (isIOS) { 383 | this.loadFromIOSData({ 384 | elevation: this.elevation, 385 | maskToBounds: this.maskToBounds, 386 | shadowColor: this.shadowColor, 387 | shadowOffset: this.shadowOffset, 388 | shadowOpacity: this.shadowOpacity, 389 | shadowRadius: this.shadowRadius, 390 | rasterize: this.rasterize, 391 | useShadowPath: this.useShadowPath 392 | }); 393 | } 394 | } 395 | this.bindEvents(); 396 | } 397 | } 398 | addIOSWrapper() { 399 | if (isIOS) { 400 | const originalElement = this.el; 401 | const parent = originalElement.parentNode; 402 | const vm = new Vue({ 403 | template: '', 404 | }).$mount(); 405 | const wrapper = vm.$el; 406 | parent.insertBefore(wrapper, originalElement); 407 | parent.removeChild(originalElement); 408 | wrapper.appendChild(originalElement); 409 | this.iosShadowWrapper = wrapper._nativeView; 410 | } 411 | } 412 | onUpdate(values) { 413 | if (this.loaded && !!values) { 414 | if (typeof values !== 'object' && (typeof values === 'string' || typeof values === 'number')) { 415 | this.shadow = values; 416 | this.elevation = values; 417 | } 418 | if (typeof values === 'object' && values.elevation) { 419 | this.shadow = values; 420 | this.elevation = this.shadow.elevation; 421 | if (isAndroid) { 422 | this.loadFromAndroidData(this.shadow); 423 | } 424 | else if (isIOS) { 425 | this.loadFromIOSData(this.shadow); 426 | } 427 | } 428 | this.applyShadow(); 429 | } 430 | } 431 | destroy() { 432 | if (this.initialized) { 433 | this.unload(); 434 | this.unbindEvents(); 435 | this.initialized = false; 436 | } 437 | } 438 | } 439 | const ShadowDirective = { 440 | bind(el, binding, vnode) { 441 | const shadowDir = new NativeShadowDirective(el, binding); 442 | shadowDir.init(); 443 | el.__vShadow = shadowDir; 444 | }, 445 | inserted(el, binding, vnode) { 446 | const shadowDir = el.__vShadow; 447 | shadowDir.addIOSWrapper(); 448 | }, 449 | update(el, { value }, vnode) { 450 | const shadowDir = el.__vShadow; 451 | shadowDir.onUpdate(value); 452 | }, 453 | unbind(el, binding, vnode) { 454 | const shadowDir = el.__vShadow; 455 | shadowDir.destroy(); 456 | el.__vShadow = null; 457 | }, 458 | }; 459 | 460 | var Elevation; 461 | (function (Elevation) { 462 | Elevation[Elevation["SWITCH"] = 1] = "SWITCH"; 463 | Elevation[Elevation["CARD_RESTING"] = 2] = "CARD_RESTING"; 464 | Elevation[Elevation["RAISED_BUTTON_RESTING"] = 2] = "RAISED_BUTTON_RESTING"; 465 | Elevation[Elevation["SEARCH_BAR_RESTING"] = 2] = "SEARCH_BAR_RESTING"; 466 | Elevation[Elevation["REFRESH_INDICADOR"] = 3] = "REFRESH_INDICADOR"; 467 | Elevation[Elevation["SEARCH_BAR_SCROLLED"] = 3] = "SEARCH_BAR_SCROLLED"; 468 | Elevation[Elevation["APPBAR"] = 4] = "APPBAR"; 469 | Elevation[Elevation["FAB_RESTING"] = 6] = "FAB_RESTING"; 470 | Elevation[Elevation["SNACKBAR"] = 6] = "SNACKBAR"; 471 | Elevation[Elevation["BOTTOM_NAVIGATION_BAR"] = 8] = "BOTTOM_NAVIGATION_BAR"; 472 | Elevation[Elevation["MENU"] = 8] = "MENU"; 473 | Elevation[Elevation["CARD_PICKED_UP"] = 8] = "CARD_PICKED_UP"; 474 | Elevation[Elevation["RAISED_BUTTON_PRESSED"] = 8] = "RAISED_BUTTON_PRESSED"; 475 | Elevation[Elevation["SUBMENU_LEVEL1"] = 9] = "SUBMENU_LEVEL1"; 476 | Elevation[Elevation["SUBMENU_LEVEL2"] = 10] = "SUBMENU_LEVEL2"; 477 | Elevation[Elevation["SUBMENU_LEVEL3"] = 11] = "SUBMENU_LEVEL3"; 478 | Elevation[Elevation["SUBMENU_LEVEL4"] = 12] = "SUBMENU_LEVEL4"; 479 | Elevation[Elevation["SUBMENU_LEVEL5"] = 13] = "SUBMENU_LEVEL5"; 480 | Elevation[Elevation["FAB_PRESSED"] = 12] = "FAB_PRESSED"; 481 | Elevation[Elevation["NAV_DRAWER"] = 16] = "NAV_DRAWER"; 482 | Elevation[Elevation["RIGHT_DRAWER"] = 16] = "RIGHT_DRAWER"; 483 | Elevation[Elevation["MODAL_BOTTOM_SHEET"] = 16] = "MODAL_BOTTOM_SHEET"; 484 | Elevation[Elevation["DIALOG"] = 24] = "DIALOG"; 485 | Elevation[Elevation["PICKER"] = 24] = "PICKER"; 486 | })(Elevation || (Elevation = {})); 487 | 488 | function install(Vue) { 489 | if (install.installed) { 490 | console.log('not installed'); 491 | return; 492 | } 493 | else { 494 | install.installed = true; 495 | Vue.directive('shadow', ShadowDirective); 496 | } 497 | } 498 | class NSVueShadow { 499 | } 500 | (function (install) { 501 | })(install || (install = {})); 502 | NSVueShadow.install = install; 503 | let GlobalVue; 504 | if (typeof window !== "undefined" && typeof window.Vue !== 'undefined') { 505 | GlobalVue = window.Vue; 506 | } 507 | else if (typeof global !== "undefined" && typeof global['Vue'] !== 'undefined') { 508 | GlobalVue = global['Vue']; 509 | } 510 | if (GlobalVue) { 511 | GlobalVue.use(NSVueShadow); 512 | } 513 | 514 | export default NSVueShadow; 515 | export { Elevation, Shadow, ShapeEnum, install }; 516 | -------------------------------------------------------------------------------- /dist/nativescript-vue-shadow.js: -------------------------------------------------------------------------------- 1 | var NativescriptVueshadow=(function(exports,Vue,platform,color,page,weakEventListener){'use strict';Vue=Vue&&Vue.hasOwnProperty('default')?Vue['default']:Vue;(function (ShapeEnum) { 2 | ShapeEnum["RECTANGLE"] = "RECTANGLE"; 3 | ShapeEnum["OVAL"] = "OVAL"; 4 | ShapeEnum["RING"] = "RING"; 5 | ShapeEnum["LINE"] = "LINE"; 6 | })(exports.ShapeEnum || (exports.ShapeEnum = {}));let LayeredShadow; 7 | let PlainShadow; 8 | if (platform.isAndroid) { 9 | LayeredShadow = android.graphics.drawable.LayerDrawable.extend({}); 10 | PlainShadow = android.graphics.drawable.GradientDrawable.extend({}); 11 | } 12 | const classCache = {}; 13 | function getAndroidR(rtype, field) { 14 | const className = "android.R$" + rtype; 15 | if (!classCache.hasOwnProperty(className)) { 16 | classCache[className] = { 17 | class: java.lang.Class.forName(className), 18 | fieldCache: {} 19 | }; 20 | } 21 | if (!classCache[className].fieldCache.hasOwnProperty(field)) { 22 | classCache[className].fieldCache[field] = +classCache[className].class.getField(field).get(null); 23 | } 24 | return classCache[className].fieldCache[field]; 25 | } 26 | class Shadow { 27 | static apply(tnsView, data) { 28 | const LOLLIPOP = 21; 29 | if (tnsView.android && 30 | android.os.Build.VERSION.SDK_INT >= LOLLIPOP) { 31 | Shadow.applyOnAndroid(tnsView, Shadow.getDefaults(data)); 32 | } 33 | else if (tnsView.ios) { 34 | Shadow.applyOnIOS(tnsView, Shadow.getDefaults(data)); 35 | } 36 | } 37 | static getDefaults(data) { 38 | return Object.assign({}, data, { 39 | shape: data.shape || Shadow.DEFAULT_SHAPE, 40 | pressedElevation: data.pressedElevation || Shadow.DEFAULT_PRESSED_ELEVATION, 41 | pressedTranslationZ: data.pressedTranslationZ || Shadow.DEFAULT_PRESSED_ELEVATION, 42 | shadowColor: data.shadowColor || 43 | Shadow.DEFAULT_SHADOW_COLOR, 44 | useShadowPath: (data.useShadowPath !== undefined ? data.useShadowPath : true), 45 | rasterize: (data.rasterize !== undefined ? data.rasterize : false) 46 | }); 47 | } 48 | static isShadow(drawable) { 49 | return (drawable instanceof LayeredShadow || drawable instanceof PlainShadow); 50 | } 51 | static applyOnAndroid(tnsView, data) { 52 | const nativeView = tnsView.android; 53 | let shape; 54 | let overrideBackground = true; 55 | let currentBg = nativeView.getBackground(); 56 | if (currentBg instanceof android.graphics.drawable.RippleDrawable) { 57 | let rippleBg = currentBg.getDrawable(0); 58 | if (rippleBg instanceof android.graphics.drawable.InsetDrawable) { 59 | overrideBackground = false; 60 | } 61 | else if (Shadow.isShadow(rippleBg)) { 62 | currentBg = rippleBg; 63 | } 64 | } 65 | if (overrideBackground) { 66 | if (Shadow.isShadow(currentBg)) { 67 | currentBg = currentBg instanceof LayeredShadow ? 68 | currentBg.getDrawable(1) : null; 69 | } 70 | const outerRadii = Array.create("float", 8); 71 | if (data.cornerRadius === undefined) { 72 | outerRadii[0] = outerRadii[1] = page.Length.toDevicePixels(tnsView.borderTopLeftRadius, 0); 73 | outerRadii[2] = outerRadii[3] = page.Length.toDevicePixels(tnsView.borderTopRightRadius, 0); 74 | outerRadii[4] = outerRadii[5] = page.Length.toDevicePixels(tnsView.borderBottomRightRadius, 0); 75 | outerRadii[6] = outerRadii[7] = page.Length.toDevicePixels(tnsView.borderBottomLeftRadius, 0); 76 | } 77 | else { 78 | java.util.Arrays.fill(outerRadii, Shadow.androidDipToPx(nativeView, data.cornerRadius)); 79 | } 80 | const bgColor = currentBg ? 81 | (currentBg instanceof android.graphics.drawable.ColorDrawable && currentBg.getColor() ? 82 | currentBg.getColor() : android.graphics.Color.parseColor(data.bgcolor || Shadow.DEFAULT_BGCOLOR)) : 83 | android.graphics.Color.parseColor(data.bgcolor || Shadow.DEFAULT_BGCOLOR); 84 | let newBg; 85 | if (data.shape !== exports.ShapeEnum.RECTANGLE || data.bgcolor || !currentBg) { 86 | shape = new PlainShadow(); 87 | shape.setShape(android.graphics.drawable.GradientDrawable[data.shape]); 88 | shape.setCornerRadii(outerRadii); 89 | shape.setColor(bgColor); 90 | newBg = shape; 91 | } 92 | else { 93 | const r = new android.graphics.drawable.shapes.RoundRectShape(outerRadii, null, null); 94 | shape = new android.graphics.drawable.ShapeDrawable(r); 95 | shape.getPaint().setColor(bgColor); 96 | var arr = Array.create(android.graphics.drawable.Drawable, 2); 97 | arr[0] = shape; 98 | arr[1] = currentBg; 99 | const drawable = new LayeredShadow(arr); 100 | newBg = drawable; 101 | } 102 | nativeView.setBackgroundDrawable(newBg); 103 | } 104 | nativeView.setElevation(Shadow.androidDipToPx(nativeView, data.elevation)); 105 | nativeView.setTranslationZ(Shadow.androidDipToPx(nativeView, data.translationZ)); 106 | if (nativeView.getStateListAnimator() || data.forcePressAnimation) { 107 | this.overrideDefaultAnimator(nativeView, data); 108 | } 109 | } 110 | static overrideDefaultAnimator(nativeView, data) { 111 | const sla = new android.animation.StateListAnimator(); 112 | const ObjectAnimator = android.animation.ObjectAnimator; 113 | const AnimatorSet = android.animation.AnimatorSet; 114 | const shortAnimTime = getAndroidR("integer", "config_shortAnimTime"); 115 | const buttonDuration = nativeView.getContext().getResources().getInteger(shortAnimTime) / 2; 116 | const pressedElevation = this.androidDipToPx(nativeView, data.pressedElevation); 117 | const pressedZ = this.androidDipToPx(nativeView, data.pressedTranslationZ); 118 | const elevation = this.androidDipToPx(nativeView, data.elevation); 119 | const z = this.androidDipToPx(nativeView, data.translationZ || 0); 120 | const pressedSet = new AnimatorSet(); 121 | const notPressedSet = new AnimatorSet(); 122 | const defaultSet = new AnimatorSet(); 123 | pressedSet.playTogether(java.util.Arrays.asList([ 124 | ObjectAnimator.ofFloat(nativeView, "translationZ", [pressedZ]) 125 | .setDuration(buttonDuration), 126 | ObjectAnimator.ofFloat(nativeView, "elevation", [pressedElevation]) 127 | .setDuration(0), 128 | ])); 129 | notPressedSet.playTogether(java.util.Arrays.asList([ 130 | ObjectAnimator.ofFloat(nativeView, "translationZ", [z]) 131 | .setDuration(buttonDuration), 132 | ObjectAnimator.ofFloat(nativeView, "elevation", [elevation]) 133 | .setDuration(0), 134 | ])); 135 | defaultSet.playTogether(java.util.Arrays.asList([ 136 | ObjectAnimator.ofFloat(nativeView, "translationZ", [0]).setDuration(0), 137 | ObjectAnimator.ofFloat(nativeView, "elevation", [0]).setDuration(0), 138 | ])); 139 | sla.addState([getAndroidR("attr", "state_pressed"), getAndroidR("attr", "state_enabled")], pressedSet); 140 | sla.addState([getAndroidR("attr", "state_enabled")], notPressedSet); 141 | sla.addState([], defaultSet); 142 | nativeView.setStateListAnimator(sla); 143 | } 144 | static applyOnIOS(tnsView, data) { 145 | const nativeView = tnsView.ios; 146 | const elevation = parseFloat((data.elevation - 0).toFixed(2)); 147 | nativeView.layer.maskToBounds = false; 148 | nativeView.layer.shadowColor = new color.Color(data.shadowColor).ios.CGColor; 149 | nativeView.layer.shadowOffset = 150 | data.shadowOffset ? 151 | CGSizeMake(0, parseFloat(String(data.shadowOffset))) : 152 | CGSizeMake(0, 0.54 * elevation - 0.14); 153 | nativeView.layer.shadowOpacity = 154 | data.shadowOpacity ? 155 | parseFloat(String(data.shadowOpacity)) : 156 | 0.006 * elevation + 0.25; 157 | nativeView.layer.shadowRadius = 158 | data.shadowRadius ? 159 | parseFloat(String(data.shadowRadius)) : 160 | 0.66 * elevation - 0.5; 161 | nativeView.layer.shouldRasterize = data.rasterize; 162 | nativeView.layer.rasterizationScale = platform.screen.mainScreen.scale; 163 | let shadowPath = null; 164 | if (data.useShadowPath) { 165 | shadowPath = UIBezierPath.bezierPathWithRoundedRectCornerRadius(nativeView.bounds, nativeView.layer.shadowRadius).cgPath; 166 | } 167 | nativeView.layer.shadowPath = shadowPath; 168 | } 169 | static androidDipToPx(nativeView, dip) { 170 | const metrics = nativeView.getContext().getResources().getDisplayMetrics(); 171 | return android.util.TypedValue.applyDimension(android.util.TypedValue.COMPLEX_UNIT_DIP, dip, metrics); 172 | } 173 | } 174 | Shadow.DEFAULT_SHAPE = exports.ShapeEnum.RECTANGLE; 175 | Shadow.DEFAULT_BGCOLOR = '#FFFFFF'; 176 | Shadow.DEFAULT_SHADOW_COLOR = '#000000'; 177 | Shadow.DEFAULT_PRESSED_ELEVATION = 2; 178 | Shadow.DEFAULT_PRESSED_Z = 4;class NativeShadowDirective { 179 | constructor(el, binding) { 180 | this.loaded = false; 181 | this.initialized = false; 182 | this.eventsBound = false; 183 | this.monkeyPatch = (val) => { 184 | this.previousNSFn.call(this.el._nativeView, val); 185 | this.applyShadow(); 186 | }; 187 | this.el = el; 188 | if (binding.value && typeof binding.value !== 'object' && (typeof binding.value === 'string' || typeof binding.value === 'number')) { 189 | this.shadow = binding.value; 190 | this.elevation = binding.value; 191 | } 192 | if (binding.value && typeof binding.value === 'object' && binding.value.elevation) { 193 | this.shadow = binding.value; 194 | this.elevation = this.shadow.elevation; 195 | if (platform.isAndroid && (('pressedElevation' in this.shadow) || 196 | ('shape' in this.shadow) || 197 | ('bgcolor' in this.shadow) || 198 | ('cornerRadius' in this.shadow) || 199 | ('translationZ' in this.shadow) || 200 | ('pressedTranslationZ' in this.shadow) || 201 | ('forcePressAnimation' in this.shadow))) { 202 | this.pressedElevation = this.shadow.pressedElevation; 203 | this.shape = this.shadow.shape; 204 | this.bgcolor = this.shadow.bgcolor; 205 | this.cornerRadius = this.shadow.cornerRadius; 206 | this.translationZ = this.shadow.translationZ; 207 | this.pressedTranslationZ = this.shadow.pressedTranslationZ; 208 | this.forcePressAnimation = this.shadow.forcePressAnimation; 209 | } 210 | else if (platform.isIOS && (('maskToBounds' in this.shadow) || 211 | ('shadowColor' in this.shadow) || 212 | ('shadowOffset' in this.shadow) || 213 | ('shadowOpacity' in this.shadow) || 214 | ('shadowRadius' in this.shadow) || 215 | ('useShadowPath' in this.shadow) || 216 | ('rasterize' in this.shadow))) { 217 | this.maskToBounds = this.shadow.maskToBounds; 218 | this.shadowColor = this.shadow.shadowColor; 219 | this.shadowOffset = this.shadow.shadowOffset; 220 | this.shadowOpacity = this.shadow.shadowOpacity; 221 | this.shadowRadius = this.shadow.shadowRadius; 222 | this.useShadowPath = this.shadow.useShadowPath; 223 | this.rasterize = this.shadow.rasterize; 224 | } 225 | } 226 | if (platform.isAndroid) { 227 | if (this.el._nativeView._redrawNativeBackground) { 228 | this.originalNSFn = this.el._nativeView._redrawNativeBackground; 229 | } 230 | } 231 | } 232 | initializeCommonData() { 233 | const tShadow = typeof this.shadow; 234 | if ((tShadow === 'string' || tShadow === 'number') && !this.elevation) { 235 | this.elevation = this.shadow ? parseInt(this.shadow, 10) : 2; 236 | } 237 | const tElevation = typeof this.elevation; 238 | if (tElevation === 'string' || tElevation === 'number') { 239 | this.elevation = this.elevation ? parseInt(this.elevation, 10) : 2; 240 | } 241 | } 242 | initializeAndroidData() { 243 | if (typeof this.cornerRadius === 'string') { 244 | this.cornerRadius = parseInt(this.cornerRadius, 10); 245 | } 246 | if (typeof this.translationZ === 'string') { 247 | this.translationZ = parseInt(this.translationZ, 10); 248 | } 249 | } 250 | initializeIOSData() { 251 | if (typeof this.shadowOffset === 'string') { 252 | this.shadowOffset = parseFloat(this.shadowOffset); 253 | } 254 | if (typeof this.shadowOpacity === 'string') { 255 | this.shadowOpacity = parseFloat(this.shadowOpacity); 256 | } 257 | if (typeof this.shadowRadius === 'string') { 258 | this.shadowRadius = parseFloat(this.shadowRadius); 259 | } 260 | } 261 | bindEvents() { 262 | if (!this.eventsBound) { 263 | weakEventListener.addWeakEventListener(this.el._nativeView, page.View.loadedEvent, this.load, this); 264 | weakEventListener.addWeakEventListener(this.el._nativeView, page.View.unloadedEvent, this.unload, this); 265 | this.eventsBound = true; 266 | if (this.el._nativeView.isLoaded) { 267 | this.load(); 268 | } 269 | } 270 | } 271 | unbindEvents() { 272 | if (this.eventsBound) { 273 | weakEventListener.removeWeakEventListener(this.el._nativeView, page.View.loadedEvent, this.load, this); 274 | weakEventListener.removeWeakEventListener(this.el._nativeView, page.View.unloadedEvent, this.unload, this); 275 | this.eventsBound = false; 276 | } 277 | } 278 | load() { 279 | this.loaded = true; 280 | this.applyShadow(); 281 | if (platform.isAndroid) { 282 | this.previousNSFn = this.el._nativeView._redrawNativeBackground; 283 | this.el._nativeView._redrawNativeBackground = this.monkeyPatch; 284 | } 285 | } 286 | unload() { 287 | this.loaded = false; 288 | if (platform.isAndroid) { 289 | this.el._nativeView._redrawNativeBackground = this.originalNSFn; 290 | } 291 | } 292 | applyShadow() { 293 | if (!this.shadow && !this.elevation) { 294 | return; 295 | } 296 | if (platform.isAndroid) { 297 | if (android.os.Build.VERSION.SDK_INT < 21) { 298 | return; 299 | } 300 | } 301 | const viewToApplyShadowTo = platform.isIOS ? this.iosShadowWrapper : this.el._nativeView; 302 | if (viewToApplyShadowTo) { 303 | Shadow.apply(viewToApplyShadowTo, { 304 | elevation: this.elevation, 305 | pressedElevation: this.pressedElevation, 306 | shape: this.shape, 307 | bgcolor: this.bgcolor, 308 | cornerRadius: this.cornerRadius, 309 | translationZ: this.translationZ, 310 | pressedTranslationZ: this.pressedTranslationZ, 311 | forcePressAnimation: this.forcePressAnimation, 312 | maskToBounds: this.maskToBounds, 313 | shadowColor: this.shadowColor, 314 | shadowOffset: this.shadowOffset, 315 | shadowOpacity: this.shadowOpacity, 316 | shadowRadius: this.shadowRadius, 317 | rasterize: this.rasterize, 318 | useShadowPath: this.useShadowPath 319 | }); 320 | } 321 | } 322 | loadFromAndroidData(data) { 323 | this.elevation = data.elevation || this.elevation; 324 | this.shape = data.shape || this.shape; 325 | this.bgcolor = data.bgcolor || this.bgcolor; 326 | this.cornerRadius = data.cornerRadius || this.cornerRadius; 327 | this.translationZ = data.translationZ || this.translationZ; 328 | this.pressedTranslationZ = data.pressedTranslationZ || this.pressedTranslationZ; 329 | this.forcePressAnimation = data.forcePressAnimation || this.forcePressAnimation; 330 | } 331 | loadFromIOSData(data) { 332 | this.maskToBounds = data.maskToBounds || this.maskToBounds; 333 | this.shadowColor = data.shadowColor || this.shadowColor; 334 | this.shadowOffset = data.shadowOffset || this.shadowOffset; 335 | this.shadowOpacity = data.shadowOpacity || this.shadowOpacity; 336 | this.shadowRadius = data.shadowRadius || this.shadowRadius; 337 | this.rasterize = data.rasterize || this.rasterize; 338 | this.useShadowPath = data.useShadowPath || this.useShadowPath; 339 | } 340 | init() { 341 | if (!this.initialized) { 342 | this.initialized = true; 343 | this.initializeCommonData(); 344 | if (platform.isAndroid) { 345 | this.initializeAndroidData(); 346 | } 347 | else if (platform.isIOS) { 348 | this.initializeIOSData(); 349 | } 350 | if (this.shadow && this.shadow.elevation) { 351 | if (platform.isAndroid) { 352 | this.loadFromAndroidData(this.shadow); 353 | } 354 | else if (platform.isIOS) { 355 | this.loadFromIOSData(this.shadow); 356 | } 357 | } 358 | if (!this.shadow && this.elevation) { 359 | if (platform.isAndroid) { 360 | this.loadFromAndroidData({ 361 | elevation: this.elevation, 362 | pressedElevation: this.pressedElevation, 363 | shape: this.shape, 364 | bgcolor: this.bgcolor, 365 | cornerRadius: this.cornerRadius, 366 | translationZ: this.translationZ, 367 | pressedTranslationZ: this.pressedTranslationZ, 368 | forcePressAnimation: this.forcePressAnimation, 369 | }); 370 | } 371 | else if (platform.isIOS) { 372 | this.loadFromIOSData({ 373 | elevation: this.elevation, 374 | maskToBounds: this.maskToBounds, 375 | shadowColor: this.shadowColor, 376 | shadowOffset: this.shadowOffset, 377 | shadowOpacity: this.shadowOpacity, 378 | shadowRadius: this.shadowRadius, 379 | rasterize: this.rasterize, 380 | useShadowPath: this.useShadowPath 381 | }); 382 | } 383 | } 384 | this.bindEvents(); 385 | } 386 | } 387 | addIOSWrapper() { 388 | if (platform.isIOS) { 389 | const originalElement = this.el; 390 | const parent = originalElement.parentNode; 391 | const vm = new Vue({ 392 | template: '', 393 | }).$mount(); 394 | const wrapper = vm.$el; 395 | parent.insertBefore(wrapper, originalElement); 396 | parent.removeChild(originalElement); 397 | wrapper.appendChild(originalElement); 398 | this.iosShadowWrapper = wrapper._nativeView; 399 | } 400 | } 401 | onUpdate(values) { 402 | if (this.loaded && !!values) { 403 | if (typeof values !== 'object' && (typeof values === 'string' || typeof values === 'number')) { 404 | this.shadow = values; 405 | this.elevation = values; 406 | } 407 | if (typeof values === 'object' && values.elevation) { 408 | this.shadow = values; 409 | this.elevation = this.shadow.elevation; 410 | if (platform.isAndroid) { 411 | this.loadFromAndroidData(this.shadow); 412 | } 413 | else if (platform.isIOS) { 414 | this.loadFromIOSData(this.shadow); 415 | } 416 | } 417 | this.applyShadow(); 418 | } 419 | } 420 | destroy() { 421 | if (this.initialized) { 422 | this.unload(); 423 | this.unbindEvents(); 424 | this.initialized = false; 425 | } 426 | } 427 | } 428 | const ShadowDirective = { 429 | bind(el, binding, vnode) { 430 | const shadowDir = new NativeShadowDirective(el, binding); 431 | shadowDir.init(); 432 | el.__vShadow = shadowDir; 433 | }, 434 | inserted(el, binding, vnode) { 435 | const shadowDir = el.__vShadow; 436 | shadowDir.addIOSWrapper(); 437 | }, 438 | update(el, { value }, vnode) { 439 | const shadowDir = el.__vShadow; 440 | shadowDir.onUpdate(value); 441 | }, 442 | unbind(el, binding, vnode) { 443 | const shadowDir = el.__vShadow; 444 | shadowDir.destroy(); 445 | el.__vShadow = null; 446 | }, 447 | };(function (Elevation) { 448 | Elevation[Elevation["SWITCH"] = 1] = "SWITCH"; 449 | Elevation[Elevation["CARD_RESTING"] = 2] = "CARD_RESTING"; 450 | Elevation[Elevation["RAISED_BUTTON_RESTING"] = 2] = "RAISED_BUTTON_RESTING"; 451 | Elevation[Elevation["SEARCH_BAR_RESTING"] = 2] = "SEARCH_BAR_RESTING"; 452 | Elevation[Elevation["REFRESH_INDICADOR"] = 3] = "REFRESH_INDICADOR"; 453 | Elevation[Elevation["SEARCH_BAR_SCROLLED"] = 3] = "SEARCH_BAR_SCROLLED"; 454 | Elevation[Elevation["APPBAR"] = 4] = "APPBAR"; 455 | Elevation[Elevation["FAB_RESTING"] = 6] = "FAB_RESTING"; 456 | Elevation[Elevation["SNACKBAR"] = 6] = "SNACKBAR"; 457 | Elevation[Elevation["BOTTOM_NAVIGATION_BAR"] = 8] = "BOTTOM_NAVIGATION_BAR"; 458 | Elevation[Elevation["MENU"] = 8] = "MENU"; 459 | Elevation[Elevation["CARD_PICKED_UP"] = 8] = "CARD_PICKED_UP"; 460 | Elevation[Elevation["RAISED_BUTTON_PRESSED"] = 8] = "RAISED_BUTTON_PRESSED"; 461 | Elevation[Elevation["SUBMENU_LEVEL1"] = 9] = "SUBMENU_LEVEL1"; 462 | Elevation[Elevation["SUBMENU_LEVEL2"] = 10] = "SUBMENU_LEVEL2"; 463 | Elevation[Elevation["SUBMENU_LEVEL3"] = 11] = "SUBMENU_LEVEL3"; 464 | Elevation[Elevation["SUBMENU_LEVEL4"] = 12] = "SUBMENU_LEVEL4"; 465 | Elevation[Elevation["SUBMENU_LEVEL5"] = 13] = "SUBMENU_LEVEL5"; 466 | Elevation[Elevation["FAB_PRESSED"] = 12] = "FAB_PRESSED"; 467 | Elevation[Elevation["NAV_DRAWER"] = 16] = "NAV_DRAWER"; 468 | Elevation[Elevation["RIGHT_DRAWER"] = 16] = "RIGHT_DRAWER"; 469 | Elevation[Elevation["MODAL_BOTTOM_SHEET"] = 16] = "MODAL_BOTTOM_SHEET"; 470 | Elevation[Elevation["DIALOG"] = 24] = "DIALOG"; 471 | Elevation[Elevation["PICKER"] = 24] = "PICKER"; 472 | })(exports.Elevation || (exports.Elevation = {}));function install(Vue) { 473 | if (install.installed) { 474 | console.log('not installed'); 475 | return; 476 | } 477 | else { 478 | install.installed = true; 479 | Vue.directive('shadow', ShadowDirective); 480 | } 481 | } 482 | class NSVueShadow { 483 | } 484 | (function (install) { 485 | })(install || (install = {})); 486 | NSVueShadow.install = install; 487 | let GlobalVue; 488 | if (typeof window !== "undefined" && typeof window.Vue !== 'undefined') { 489 | GlobalVue = window.Vue; 490 | } 491 | else if (typeof global !== "undefined" && typeof global['Vue'] !== 'undefined') { 492 | GlobalVue = global['Vue']; 493 | } 494 | if (GlobalVue) { 495 | GlobalVue.use(NSVueShadow); 496 | }exports.Shadow=Shadow;exports.default=NSVueShadow;exports.install=install;return exports;}({},vue,platform,color,page,weakEventListener)); -------------------------------------------------------------------------------- /dist/nativescript-vue-shadow.umd.js: -------------------------------------------------------------------------------- 1 | (function(g,f){typeof exports==='object'&&typeof module!=='undefined'?f(exports,require('nativescript-vue'),require('tns-core-modules/platform'),require('tns-core-modules/color'),require('tns-core-modules/ui/page/page'),require('tns-core-modules/ui/core/weak-event-listener')):typeof define==='function'&&define.amd?define(['exports','nativescript-vue','tns-core-modules/platform','tns-core-modules/color','tns-core-modules/ui/page/page','tns-core-modules/ui/core/weak-event-listener'],f):(g=g||self,f(g.NativescriptVueshadow={},g.vue,g.platform,g.color,g.page,g.weakEventListener));}(this,function(exports, Vue, platform, color, page, weakEventListener){'use strict';Vue=Vue&&Vue.hasOwnProperty('default')?Vue['default']:Vue;(function (ShapeEnum) { 2 | ShapeEnum["RECTANGLE"] = "RECTANGLE"; 3 | ShapeEnum["OVAL"] = "OVAL"; 4 | ShapeEnum["RING"] = "RING"; 5 | ShapeEnum["LINE"] = "LINE"; 6 | })(exports.ShapeEnum || (exports.ShapeEnum = {}));let LayeredShadow; 7 | let PlainShadow; 8 | if (platform.isAndroid) { 9 | LayeredShadow = android.graphics.drawable.LayerDrawable.extend({}); 10 | PlainShadow = android.graphics.drawable.GradientDrawable.extend({}); 11 | } 12 | const classCache = {}; 13 | function getAndroidR(rtype, field) { 14 | const className = "android.R$" + rtype; 15 | if (!classCache.hasOwnProperty(className)) { 16 | classCache[className] = { 17 | class: java.lang.Class.forName(className), 18 | fieldCache: {} 19 | }; 20 | } 21 | if (!classCache[className].fieldCache.hasOwnProperty(field)) { 22 | classCache[className].fieldCache[field] = +classCache[className].class.getField(field).get(null); 23 | } 24 | return classCache[className].fieldCache[field]; 25 | } 26 | class Shadow { 27 | static apply(tnsView, data) { 28 | const LOLLIPOP = 21; 29 | if (tnsView.android && 30 | android.os.Build.VERSION.SDK_INT >= LOLLIPOP) { 31 | Shadow.applyOnAndroid(tnsView, Shadow.getDefaults(data)); 32 | } 33 | else if (tnsView.ios) { 34 | Shadow.applyOnIOS(tnsView, Shadow.getDefaults(data)); 35 | } 36 | } 37 | static getDefaults(data) { 38 | return Object.assign({}, data, { 39 | shape: data.shape || Shadow.DEFAULT_SHAPE, 40 | pressedElevation: data.pressedElevation || Shadow.DEFAULT_PRESSED_ELEVATION, 41 | pressedTranslationZ: data.pressedTranslationZ || Shadow.DEFAULT_PRESSED_ELEVATION, 42 | shadowColor: data.shadowColor || 43 | Shadow.DEFAULT_SHADOW_COLOR, 44 | useShadowPath: (data.useShadowPath !== undefined ? data.useShadowPath : true), 45 | rasterize: (data.rasterize !== undefined ? data.rasterize : false) 46 | }); 47 | } 48 | static isShadow(drawable) { 49 | return (drawable instanceof LayeredShadow || drawable instanceof PlainShadow); 50 | } 51 | static applyOnAndroid(tnsView, data) { 52 | const nativeView = tnsView.android; 53 | let shape; 54 | let overrideBackground = true; 55 | let currentBg = nativeView.getBackground(); 56 | if (currentBg instanceof android.graphics.drawable.RippleDrawable) { 57 | let rippleBg = currentBg.getDrawable(0); 58 | if (rippleBg instanceof android.graphics.drawable.InsetDrawable) { 59 | overrideBackground = false; 60 | } 61 | else if (Shadow.isShadow(rippleBg)) { 62 | currentBg = rippleBg; 63 | } 64 | } 65 | if (overrideBackground) { 66 | if (Shadow.isShadow(currentBg)) { 67 | currentBg = currentBg instanceof LayeredShadow ? 68 | currentBg.getDrawable(1) : null; 69 | } 70 | const outerRadii = Array.create("float", 8); 71 | if (data.cornerRadius === undefined) { 72 | outerRadii[0] = outerRadii[1] = page.Length.toDevicePixels(tnsView.borderTopLeftRadius, 0); 73 | outerRadii[2] = outerRadii[3] = page.Length.toDevicePixels(tnsView.borderTopRightRadius, 0); 74 | outerRadii[4] = outerRadii[5] = page.Length.toDevicePixels(tnsView.borderBottomRightRadius, 0); 75 | outerRadii[6] = outerRadii[7] = page.Length.toDevicePixels(tnsView.borderBottomLeftRadius, 0); 76 | } 77 | else { 78 | java.util.Arrays.fill(outerRadii, Shadow.androidDipToPx(nativeView, data.cornerRadius)); 79 | } 80 | const bgColor = currentBg ? 81 | (currentBg instanceof android.graphics.drawable.ColorDrawable && currentBg.getColor() ? 82 | currentBg.getColor() : android.graphics.Color.parseColor(data.bgcolor || Shadow.DEFAULT_BGCOLOR)) : 83 | android.graphics.Color.parseColor(data.bgcolor || Shadow.DEFAULT_BGCOLOR); 84 | let newBg; 85 | if (data.shape !== exports.ShapeEnum.RECTANGLE || data.bgcolor || !currentBg) { 86 | shape = new PlainShadow(); 87 | shape.setShape(android.graphics.drawable.GradientDrawable[data.shape]); 88 | shape.setCornerRadii(outerRadii); 89 | shape.setColor(bgColor); 90 | newBg = shape; 91 | } 92 | else { 93 | const r = new android.graphics.drawable.shapes.RoundRectShape(outerRadii, null, null); 94 | shape = new android.graphics.drawable.ShapeDrawable(r); 95 | shape.getPaint().setColor(bgColor); 96 | var arr = Array.create(android.graphics.drawable.Drawable, 2); 97 | arr[0] = shape; 98 | arr[1] = currentBg; 99 | const drawable = new LayeredShadow(arr); 100 | newBg = drawable; 101 | } 102 | nativeView.setBackgroundDrawable(newBg); 103 | } 104 | nativeView.setElevation(Shadow.androidDipToPx(nativeView, data.elevation)); 105 | nativeView.setTranslationZ(Shadow.androidDipToPx(nativeView, data.translationZ)); 106 | if (nativeView.getStateListAnimator() || data.forcePressAnimation) { 107 | this.overrideDefaultAnimator(nativeView, data); 108 | } 109 | } 110 | static overrideDefaultAnimator(nativeView, data) { 111 | const sla = new android.animation.StateListAnimator(); 112 | const ObjectAnimator = android.animation.ObjectAnimator; 113 | const AnimatorSet = android.animation.AnimatorSet; 114 | const shortAnimTime = getAndroidR("integer", "config_shortAnimTime"); 115 | const buttonDuration = nativeView.getContext().getResources().getInteger(shortAnimTime) / 2; 116 | const pressedElevation = this.androidDipToPx(nativeView, data.pressedElevation); 117 | const pressedZ = this.androidDipToPx(nativeView, data.pressedTranslationZ); 118 | const elevation = this.androidDipToPx(nativeView, data.elevation); 119 | const z = this.androidDipToPx(nativeView, data.translationZ || 0); 120 | const pressedSet = new AnimatorSet(); 121 | const notPressedSet = new AnimatorSet(); 122 | const defaultSet = new AnimatorSet(); 123 | pressedSet.playTogether(java.util.Arrays.asList([ 124 | ObjectAnimator.ofFloat(nativeView, "translationZ", [pressedZ]) 125 | .setDuration(buttonDuration), 126 | ObjectAnimator.ofFloat(nativeView, "elevation", [pressedElevation]) 127 | .setDuration(0), 128 | ])); 129 | notPressedSet.playTogether(java.util.Arrays.asList([ 130 | ObjectAnimator.ofFloat(nativeView, "translationZ", [z]) 131 | .setDuration(buttonDuration), 132 | ObjectAnimator.ofFloat(nativeView, "elevation", [elevation]) 133 | .setDuration(0), 134 | ])); 135 | defaultSet.playTogether(java.util.Arrays.asList([ 136 | ObjectAnimator.ofFloat(nativeView, "translationZ", [0]).setDuration(0), 137 | ObjectAnimator.ofFloat(nativeView, "elevation", [0]).setDuration(0), 138 | ])); 139 | sla.addState([getAndroidR("attr", "state_pressed"), getAndroidR("attr", "state_enabled")], pressedSet); 140 | sla.addState([getAndroidR("attr", "state_enabled")], notPressedSet); 141 | sla.addState([], defaultSet); 142 | nativeView.setStateListAnimator(sla); 143 | } 144 | static applyOnIOS(tnsView, data) { 145 | const nativeView = tnsView.ios; 146 | const elevation = parseFloat((data.elevation - 0).toFixed(2)); 147 | nativeView.layer.maskToBounds = false; 148 | nativeView.layer.shadowColor = new color.Color(data.shadowColor).ios.CGColor; 149 | nativeView.layer.shadowOffset = 150 | data.shadowOffset ? 151 | CGSizeMake(0, parseFloat(String(data.shadowOffset))) : 152 | CGSizeMake(0, 0.54 * elevation - 0.14); 153 | nativeView.layer.shadowOpacity = 154 | data.shadowOpacity ? 155 | parseFloat(String(data.shadowOpacity)) : 156 | 0.006 * elevation + 0.25; 157 | nativeView.layer.shadowRadius = 158 | data.shadowRadius ? 159 | parseFloat(String(data.shadowRadius)) : 160 | 0.66 * elevation - 0.5; 161 | nativeView.layer.shouldRasterize = data.rasterize; 162 | nativeView.layer.rasterizationScale = platform.screen.mainScreen.scale; 163 | let shadowPath = null; 164 | if (data.useShadowPath) { 165 | shadowPath = UIBezierPath.bezierPathWithRoundedRectCornerRadius(nativeView.bounds, nativeView.layer.shadowRadius).cgPath; 166 | } 167 | nativeView.layer.shadowPath = shadowPath; 168 | } 169 | static androidDipToPx(nativeView, dip) { 170 | const metrics = nativeView.getContext().getResources().getDisplayMetrics(); 171 | return android.util.TypedValue.applyDimension(android.util.TypedValue.COMPLEX_UNIT_DIP, dip, metrics); 172 | } 173 | } 174 | Shadow.DEFAULT_SHAPE = exports.ShapeEnum.RECTANGLE; 175 | Shadow.DEFAULT_BGCOLOR = '#FFFFFF'; 176 | Shadow.DEFAULT_SHADOW_COLOR = '#000000'; 177 | Shadow.DEFAULT_PRESSED_ELEVATION = 2; 178 | Shadow.DEFAULT_PRESSED_Z = 4;class NativeShadowDirective { 179 | constructor(el, binding) { 180 | this.loaded = false; 181 | this.initialized = false; 182 | this.eventsBound = false; 183 | this.monkeyPatch = (val) => { 184 | this.previousNSFn.call(this.el._nativeView, val); 185 | this.applyShadow(); 186 | }; 187 | this.el = el; 188 | if (binding.value && typeof binding.value !== 'object' && (typeof binding.value === 'string' || typeof binding.value === 'number')) { 189 | this.shadow = binding.value; 190 | this.elevation = binding.value; 191 | } 192 | if (binding.value && typeof binding.value === 'object' && binding.value.elevation) { 193 | this.shadow = binding.value; 194 | this.elevation = this.shadow.elevation; 195 | if (platform.isAndroid && (('pressedElevation' in this.shadow) || 196 | ('shape' in this.shadow) || 197 | ('bgcolor' in this.shadow) || 198 | ('cornerRadius' in this.shadow) || 199 | ('translationZ' in this.shadow) || 200 | ('pressedTranslationZ' in this.shadow) || 201 | ('forcePressAnimation' in this.shadow))) { 202 | this.pressedElevation = this.shadow.pressedElevation; 203 | this.shape = this.shadow.shape; 204 | this.bgcolor = this.shadow.bgcolor; 205 | this.cornerRadius = this.shadow.cornerRadius; 206 | this.translationZ = this.shadow.translationZ; 207 | this.pressedTranslationZ = this.shadow.pressedTranslationZ; 208 | this.forcePressAnimation = this.shadow.forcePressAnimation; 209 | } 210 | else if (platform.isIOS && (('maskToBounds' in this.shadow) || 211 | ('shadowColor' in this.shadow) || 212 | ('shadowOffset' in this.shadow) || 213 | ('shadowOpacity' in this.shadow) || 214 | ('shadowRadius' in this.shadow) || 215 | ('useShadowPath' in this.shadow) || 216 | ('rasterize' in this.shadow))) { 217 | this.maskToBounds = this.shadow.maskToBounds; 218 | this.shadowColor = this.shadow.shadowColor; 219 | this.shadowOffset = this.shadow.shadowOffset; 220 | this.shadowOpacity = this.shadow.shadowOpacity; 221 | this.shadowRadius = this.shadow.shadowRadius; 222 | this.useShadowPath = this.shadow.useShadowPath; 223 | this.rasterize = this.shadow.rasterize; 224 | } 225 | } 226 | if (platform.isAndroid) { 227 | if (this.el._nativeView._redrawNativeBackground) { 228 | this.originalNSFn = this.el._nativeView._redrawNativeBackground; 229 | } 230 | } 231 | } 232 | initializeCommonData() { 233 | const tShadow = typeof this.shadow; 234 | if ((tShadow === 'string' || tShadow === 'number') && !this.elevation) { 235 | this.elevation = this.shadow ? parseInt(this.shadow, 10) : 2; 236 | } 237 | const tElevation = typeof this.elevation; 238 | if (tElevation === 'string' || tElevation === 'number') { 239 | this.elevation = this.elevation ? parseInt(this.elevation, 10) : 2; 240 | } 241 | } 242 | initializeAndroidData() { 243 | if (typeof this.cornerRadius === 'string') { 244 | this.cornerRadius = parseInt(this.cornerRadius, 10); 245 | } 246 | if (typeof this.translationZ === 'string') { 247 | this.translationZ = parseInt(this.translationZ, 10); 248 | } 249 | } 250 | initializeIOSData() { 251 | if (typeof this.shadowOffset === 'string') { 252 | this.shadowOffset = parseFloat(this.shadowOffset); 253 | } 254 | if (typeof this.shadowOpacity === 'string') { 255 | this.shadowOpacity = parseFloat(this.shadowOpacity); 256 | } 257 | if (typeof this.shadowRadius === 'string') { 258 | this.shadowRadius = parseFloat(this.shadowRadius); 259 | } 260 | } 261 | bindEvents() { 262 | if (!this.eventsBound) { 263 | weakEventListener.addWeakEventListener(this.el._nativeView, page.View.loadedEvent, this.load, this); 264 | weakEventListener.addWeakEventListener(this.el._nativeView, page.View.unloadedEvent, this.unload, this); 265 | this.eventsBound = true; 266 | if (this.el._nativeView.isLoaded) { 267 | this.load(); 268 | } 269 | } 270 | } 271 | unbindEvents() { 272 | if (this.eventsBound) { 273 | weakEventListener.removeWeakEventListener(this.el._nativeView, page.View.loadedEvent, this.load, this); 274 | weakEventListener.removeWeakEventListener(this.el._nativeView, page.View.unloadedEvent, this.unload, this); 275 | this.eventsBound = false; 276 | } 277 | } 278 | load() { 279 | this.loaded = true; 280 | this.applyShadow(); 281 | if (platform.isAndroid) { 282 | this.previousNSFn = this.el._nativeView._redrawNativeBackground; 283 | this.el._nativeView._redrawNativeBackground = this.monkeyPatch; 284 | } 285 | } 286 | unload() { 287 | this.loaded = false; 288 | if (platform.isAndroid) { 289 | this.el._nativeView._redrawNativeBackground = this.originalNSFn; 290 | } 291 | } 292 | applyShadow() { 293 | if (!this.shadow && !this.elevation) { 294 | return; 295 | } 296 | if (platform.isAndroid) { 297 | if (android.os.Build.VERSION.SDK_INT < 21) { 298 | return; 299 | } 300 | } 301 | const viewToApplyShadowTo = platform.isIOS ? this.iosShadowWrapper : this.el._nativeView; 302 | if (viewToApplyShadowTo) { 303 | Shadow.apply(viewToApplyShadowTo, { 304 | elevation: this.elevation, 305 | pressedElevation: this.pressedElevation, 306 | shape: this.shape, 307 | bgcolor: this.bgcolor, 308 | cornerRadius: this.cornerRadius, 309 | translationZ: this.translationZ, 310 | pressedTranslationZ: this.pressedTranslationZ, 311 | forcePressAnimation: this.forcePressAnimation, 312 | maskToBounds: this.maskToBounds, 313 | shadowColor: this.shadowColor, 314 | shadowOffset: this.shadowOffset, 315 | shadowOpacity: this.shadowOpacity, 316 | shadowRadius: this.shadowRadius, 317 | rasterize: this.rasterize, 318 | useShadowPath: this.useShadowPath 319 | }); 320 | } 321 | } 322 | loadFromAndroidData(data) { 323 | this.elevation = data.elevation || this.elevation; 324 | this.shape = data.shape || this.shape; 325 | this.bgcolor = data.bgcolor || this.bgcolor; 326 | this.cornerRadius = data.cornerRadius || this.cornerRadius; 327 | this.translationZ = data.translationZ || this.translationZ; 328 | this.pressedTranslationZ = data.pressedTranslationZ || this.pressedTranslationZ; 329 | this.forcePressAnimation = data.forcePressAnimation || this.forcePressAnimation; 330 | } 331 | loadFromIOSData(data) { 332 | this.maskToBounds = data.maskToBounds || this.maskToBounds; 333 | this.shadowColor = data.shadowColor || this.shadowColor; 334 | this.shadowOffset = data.shadowOffset || this.shadowOffset; 335 | this.shadowOpacity = data.shadowOpacity || this.shadowOpacity; 336 | this.shadowRadius = data.shadowRadius || this.shadowRadius; 337 | this.rasterize = data.rasterize || this.rasterize; 338 | this.useShadowPath = data.useShadowPath || this.useShadowPath; 339 | } 340 | init() { 341 | if (!this.initialized) { 342 | this.initialized = true; 343 | this.initializeCommonData(); 344 | if (platform.isAndroid) { 345 | this.initializeAndroidData(); 346 | } 347 | else if (platform.isIOS) { 348 | this.initializeIOSData(); 349 | } 350 | if (this.shadow && this.shadow.elevation) { 351 | if (platform.isAndroid) { 352 | this.loadFromAndroidData(this.shadow); 353 | } 354 | else if (platform.isIOS) { 355 | this.loadFromIOSData(this.shadow); 356 | } 357 | } 358 | if (!this.shadow && this.elevation) { 359 | if (platform.isAndroid) { 360 | this.loadFromAndroidData({ 361 | elevation: this.elevation, 362 | pressedElevation: this.pressedElevation, 363 | shape: this.shape, 364 | bgcolor: this.bgcolor, 365 | cornerRadius: this.cornerRadius, 366 | translationZ: this.translationZ, 367 | pressedTranslationZ: this.pressedTranslationZ, 368 | forcePressAnimation: this.forcePressAnimation, 369 | }); 370 | } 371 | else if (platform.isIOS) { 372 | this.loadFromIOSData({ 373 | elevation: this.elevation, 374 | maskToBounds: this.maskToBounds, 375 | shadowColor: this.shadowColor, 376 | shadowOffset: this.shadowOffset, 377 | shadowOpacity: this.shadowOpacity, 378 | shadowRadius: this.shadowRadius, 379 | rasterize: this.rasterize, 380 | useShadowPath: this.useShadowPath 381 | }); 382 | } 383 | } 384 | this.bindEvents(); 385 | } 386 | } 387 | addIOSWrapper() { 388 | if (platform.isIOS) { 389 | const originalElement = this.el; 390 | const parent = originalElement.parentNode; 391 | const vm = new Vue({ 392 | template: '', 393 | }).$mount(); 394 | const wrapper = vm.$el; 395 | parent.insertBefore(wrapper, originalElement); 396 | parent.removeChild(originalElement); 397 | wrapper.appendChild(originalElement); 398 | this.iosShadowWrapper = wrapper._nativeView; 399 | } 400 | } 401 | onUpdate(values) { 402 | if (this.loaded && !!values) { 403 | if (typeof values !== 'object' && (typeof values === 'string' || typeof values === 'number')) { 404 | this.shadow = values; 405 | this.elevation = values; 406 | } 407 | if (typeof values === 'object' && values.elevation) { 408 | this.shadow = values; 409 | this.elevation = this.shadow.elevation; 410 | if (platform.isAndroid) { 411 | this.loadFromAndroidData(this.shadow); 412 | } 413 | else if (platform.isIOS) { 414 | this.loadFromIOSData(this.shadow); 415 | } 416 | } 417 | this.applyShadow(); 418 | } 419 | } 420 | destroy() { 421 | if (this.initialized) { 422 | this.unload(); 423 | this.unbindEvents(); 424 | this.initialized = false; 425 | } 426 | } 427 | } 428 | const ShadowDirective = { 429 | bind(el, binding, vnode) { 430 | const shadowDir = new NativeShadowDirective(el, binding); 431 | shadowDir.init(); 432 | el.__vShadow = shadowDir; 433 | }, 434 | inserted(el, binding, vnode) { 435 | const shadowDir = el.__vShadow; 436 | shadowDir.addIOSWrapper(); 437 | }, 438 | update(el, { value }, vnode) { 439 | const shadowDir = el.__vShadow; 440 | shadowDir.onUpdate(value); 441 | }, 442 | unbind(el, binding, vnode) { 443 | const shadowDir = el.__vShadow; 444 | shadowDir.destroy(); 445 | el.__vShadow = null; 446 | }, 447 | };(function (Elevation) { 448 | Elevation[Elevation["SWITCH"] = 1] = "SWITCH"; 449 | Elevation[Elevation["CARD_RESTING"] = 2] = "CARD_RESTING"; 450 | Elevation[Elevation["RAISED_BUTTON_RESTING"] = 2] = "RAISED_BUTTON_RESTING"; 451 | Elevation[Elevation["SEARCH_BAR_RESTING"] = 2] = "SEARCH_BAR_RESTING"; 452 | Elevation[Elevation["REFRESH_INDICADOR"] = 3] = "REFRESH_INDICADOR"; 453 | Elevation[Elevation["SEARCH_BAR_SCROLLED"] = 3] = "SEARCH_BAR_SCROLLED"; 454 | Elevation[Elevation["APPBAR"] = 4] = "APPBAR"; 455 | Elevation[Elevation["FAB_RESTING"] = 6] = "FAB_RESTING"; 456 | Elevation[Elevation["SNACKBAR"] = 6] = "SNACKBAR"; 457 | Elevation[Elevation["BOTTOM_NAVIGATION_BAR"] = 8] = "BOTTOM_NAVIGATION_BAR"; 458 | Elevation[Elevation["MENU"] = 8] = "MENU"; 459 | Elevation[Elevation["CARD_PICKED_UP"] = 8] = "CARD_PICKED_UP"; 460 | Elevation[Elevation["RAISED_BUTTON_PRESSED"] = 8] = "RAISED_BUTTON_PRESSED"; 461 | Elevation[Elevation["SUBMENU_LEVEL1"] = 9] = "SUBMENU_LEVEL1"; 462 | Elevation[Elevation["SUBMENU_LEVEL2"] = 10] = "SUBMENU_LEVEL2"; 463 | Elevation[Elevation["SUBMENU_LEVEL3"] = 11] = "SUBMENU_LEVEL3"; 464 | Elevation[Elevation["SUBMENU_LEVEL4"] = 12] = "SUBMENU_LEVEL4"; 465 | Elevation[Elevation["SUBMENU_LEVEL5"] = 13] = "SUBMENU_LEVEL5"; 466 | Elevation[Elevation["FAB_PRESSED"] = 12] = "FAB_PRESSED"; 467 | Elevation[Elevation["NAV_DRAWER"] = 16] = "NAV_DRAWER"; 468 | Elevation[Elevation["RIGHT_DRAWER"] = 16] = "RIGHT_DRAWER"; 469 | Elevation[Elevation["MODAL_BOTTOM_SHEET"] = 16] = "MODAL_BOTTOM_SHEET"; 470 | Elevation[Elevation["DIALOG"] = 24] = "DIALOG"; 471 | Elevation[Elevation["PICKER"] = 24] = "PICKER"; 472 | })(exports.Elevation || (exports.Elevation = {}));function install(Vue) { 473 | if (install.installed) { 474 | console.log('not installed'); 475 | return; 476 | } 477 | else { 478 | install.installed = true; 479 | Vue.directive('shadow', ShadowDirective); 480 | } 481 | } 482 | class NSVueShadow { 483 | } 484 | (function (install) { 485 | })(install || (install = {})); 486 | NSVueShadow.install = install; 487 | let GlobalVue; 488 | if (typeof window !== "undefined" && typeof window.Vue !== 'undefined') { 489 | GlobalVue = window.Vue; 490 | } 491 | else if (typeof global !== "undefined" && typeof global['Vue'] !== 'undefined') { 492 | GlobalVue = global['Vue']; 493 | } 494 | if (GlobalVue) { 495 | GlobalVue.use(NSVueShadow); 496 | }exports.Shadow=Shadow;exports.default=NSVueShadow;exports.install=install;Object.defineProperty(exports,'__esModule',{value:true});})); -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | moduleFileExtensions: ["js", "jsx", "json", "vue", "ts", "tsx"], 3 | transform: { 4 | "^.+\\.vue$": "vue-jest", 5 | ".+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$": 6 | "jest-transform-stub", 7 | "^.+\\.tsx?$": "ts-jest" 8 | }, 9 | transformIgnorePatterns: ["/node_modules/"], 10 | moduleNameMapper: { 11 | "^@/(.*)$": "/src/$1" 12 | }, 13 | snapshotSerializers: ["jest-serializer-vue"], 14 | testMatch: [ 15 | "**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)" 16 | ], 17 | testURL: "http://localhost/", 18 | globals: { 19 | "ts-jest": { 20 | babelConfig: true 21 | } 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nativescript-vue-shadow", 3 | "version": "0.1.0", 4 | "description": "Nativescript-Vue Shadow Plugin", 5 | "main": "dist/nativescript-vue-shadow.umd.js", 6 | "module": "dist/nativescript-vue-shadow.esm.js", 7 | "unpkg": "dist/nativescript-vue-shadow.js", 8 | "files": [ 9 | "dist/*", 10 | "src/*", 11 | "types/*", 12 | "LICENSE" 13 | ], 14 | "typings": "types/index.d.ts", 15 | "scripts": { 16 | "build:dev": "cross-env NODE_ENV=development rollup --config build/rollup.config.js", 17 | "build": "cross-env NODE_ENV=production rollup --config build/rollup.config.js", 18 | "build:umd": "cross-env NODE_ENV=production rollup --config build/rollup.config.js --format umd", 19 | "build:es": "cross-env NODE_ENV=production rollup --config build/rollup.config.js --format es", 20 | "build:unpkg": "cross-env NODE_ENV=production rollup --config build/rollup.config.js --format iife", 21 | "test": "echo \"Error: no test specified\"", 22 | "clean": "rimraf dist/*", 23 | "preversion": "npm run clean && npm test", 24 | "version": "npm run build && git add -A", 25 | "postversion": "git push --tags origin master", 26 | "prep:major": "npm version major -m \"build %s\"", 27 | "prep:minor": "npm version minor -m \"build %s\"", 28 | "prep:patch": "npm version patch -m \"build %s\"" 29 | }, 30 | "repository": { 31 | "type": "git", 32 | "url": "git+https://github.com/jawa-the-hutt/nativescript-vue-shadow.git" 33 | }, 34 | "keywords": [ 35 | "vue", 36 | "nativescript", 37 | "typescript", 38 | "web", 39 | "code share", 40 | "shadow" 41 | ], 42 | "author": "Gary Gambill", 43 | "license": "MIT", 44 | "bugs": { 45 | "url": "https://github.com/jawa-the-hutt/nativescript-vue-shadow/issues" 46 | }, 47 | "homepage": "https://github.com/jawa-the-hutt/nativescript-vue-shadow#readme", 48 | "dependencies": { 49 | "nativescript-theme-core": "~1.0.4" 50 | }, 51 | "devDependencies": { 52 | "@babel/core": "^7.4.3", 53 | "@babel/plugin-syntax-dynamic-import": "^7.2.0", 54 | "@babel/preset-env": "^7.4.3", 55 | "@babel/preset-es2015": "^7.0.0-beta.53", 56 | "@types/jest": "^24.0.11", 57 | "@types/node": "^11.13.4", 58 | "@typescript-eslint/eslint-plugin": "^1.6.0", 59 | "@typescript-eslint/parser": "^1.6.0", 60 | "@vue/eslint-config-prettier": "^4.0.1", 61 | "@vue/eslint-config-typescript": "^4.0.0", 62 | "@vue/test-utils": "1.0.0-beta.29", 63 | "babel-eslint": "^10.0.1", 64 | "cross-env": "^5.2.0", 65 | "eslint": "^5.16.0", 66 | "eslint-config-prettier": "^4.1.0", 67 | "eslint-plugin-prettier": "^3.0.1", 68 | "eslint-plugin-react": "^7.12.4", 69 | "eslint-plugin-vue": "^5.2.2", 70 | "jest": "^24.7.1", 71 | "minimist": "^1.2.0", 72 | "nativescript-vue": "^2.2.2", 73 | "prettier": "^1.17.0", 74 | "rimraf": "^2.6.3", 75 | "rollup": "^1.10.0", 76 | "rollup-plugin-babel": "^4.3.2", 77 | "rollup-plugin-commonjs": "^9.3.4", 78 | "rollup-plugin-node-resolve": "^4.2.3", 79 | "rollup-plugin-replace": "^2.2.0", 80 | "rollup-plugin-terser": "^4.0.4", 81 | "rollup-plugin-typescript": "^1.0.1", 82 | "rollup-plugin-typescript2": "^0.20.1", 83 | "rollup-plugin-vue": "^5.0.0", 84 | "tns-core-modules": "^5.3.1", 85 | "ts-jest": "^24.0.2", 86 | "typescript": "^3.4.3", 87 | "vue": "^2.6.10", 88 | "vue-class-component": "^7.0.2", 89 | "vue-eslint-parser": "^6.0.3", 90 | "vue-property-decorator": "^8.1.0", 91 | "vue-template-compiler": "^2.6.10" 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/common/android-data.model.ts: -------------------------------------------------------------------------------- 1 | import { Shape } from './shape.enum'; 2 | 3 | export type AndroidData = { 4 | elevation: number; 5 | pressedElevation?: number; 6 | shape?: Shape; 7 | bgcolor?: string; 8 | cornerRadius?: number; 9 | translationZ?: number; 10 | pressedTranslationZ?: number; 11 | forcePressAnimation?: boolean; 12 | } -------------------------------------------------------------------------------- /src/common/elevation.enum.ts: -------------------------------------------------------------------------------- 1 | export enum Elevation { 2 | SWITCH = 1, 3 | CARD_RESTING = 2, 4 | RAISED_BUTTON_RESTING = 2, 5 | SEARCH_BAR_RESTING = 2, 6 | REFRESH_INDICADOR = 3, 7 | SEARCH_BAR_SCROLLED = 3, 8 | APPBAR = 4, 9 | FAB_RESTING = 6, 10 | SNACKBAR = 6, 11 | BOTTOM_NAVIGATION_BAR = 8, 12 | MENU = 8, 13 | CARD_PICKED_UP = 8, 14 | RAISED_BUTTON_PRESSED = 8, 15 | SUBMENU_LEVEL1 = 9, 16 | SUBMENU_LEVEL2 = 10, 17 | SUBMENU_LEVEL3 = 11, 18 | SUBMENU_LEVEL4 = 12, 19 | SUBMENU_LEVEL5 = 13, 20 | FAB_PRESSED = 12, 21 | NAV_DRAWER = 16, 22 | RIGHT_DRAWER = 16, 23 | MODAL_BOTTOM_SHEET = 16, 24 | DIALOG = 24, 25 | PICKER = 24, 26 | } -------------------------------------------------------------------------------- /src/common/index.ts: -------------------------------------------------------------------------------- 1 | export * from './android-data.model'; 2 | export * from './elevation.enum'; 3 | export * from './ios-data.model'; 4 | export * from './shadow'; 5 | export * from './shape.enum'; -------------------------------------------------------------------------------- /src/common/ios-data.model.ts: -------------------------------------------------------------------------------- 1 | export type IOSData = { 2 | elevation: number; 3 | maskToBounds?: boolean; 4 | shadowColor?: string; 5 | shadowOffset?: number; 6 | shadowOpacity?: number; 7 | shadowRadius?: number; 8 | rasterize?: boolean; 9 | useShadowPath?: boolean; 10 | } -------------------------------------------------------------------------------- /src/common/shadow.ts: -------------------------------------------------------------------------------- 1 | import { Color } from 'tns-core-modules/color'; 2 | 3 | import { AndroidData } from "./android-data.model"; 4 | import { IOSData } from "./ios-data.model"; 5 | import { ShapeEnum } from './shape.enum'; 6 | import { Length } from 'tns-core-modules/ui/page/page'; 7 | import { isAndroid, screen } from "tns-core-modules/platform"; 8 | import { Shape } from './shape.enum'; 9 | 10 | declare const android: any; 11 | declare const java: any; 12 | declare const CGSizeMake: any; 13 | declare const UIScreen: any; 14 | declare const Array: any; 15 | declare const UIBezierPath: any; 16 | 17 | let LayeredShadow; 18 | let PlainShadow; 19 | 20 | if (isAndroid) { 21 | LayeredShadow = android.graphics.drawable.LayerDrawable.extend({}); 22 | PlainShadow = android.graphics.drawable.GradientDrawable.extend({}); 23 | } 24 | 25 | const classCache: { [id: string]: { class: any, fieldCache: { [id: string]: number } } } = {}; 26 | // https://github.com/NativeScript/android-runtime/issues/1330 27 | function getAndroidR(rtype: string, field: string): number { 28 | const className = "android.R$" + rtype; 29 | if (!classCache.hasOwnProperty(className)) { 30 | classCache[className] = { 31 | class: java.lang.Class.forName(className), 32 | fieldCache: {} 33 | }; 34 | } 35 | if(!classCache[className].fieldCache.hasOwnProperty(field)) { 36 | classCache[className].fieldCache[field] = +classCache[className].class.getField(field).get(null); 37 | } 38 | return classCache[className].fieldCache[field]; 39 | } 40 | 41 | export class Shadow { 42 | static DEFAULT_SHAPE = ShapeEnum.RECTANGLE; 43 | static DEFAULT_BGCOLOR = '#FFFFFF'; 44 | static DEFAULT_SHADOW_COLOR = '#000000'; 45 | static DEFAULT_PRESSED_ELEVATION = 2; 46 | static DEFAULT_PRESSED_Z = 4; 47 | 48 | static apply(tnsView: any, data: IOSData | AndroidData) { 49 | const LOLLIPOP = 21; 50 | if ( 51 | tnsView.android && 52 | android.os.Build.VERSION.SDK_INT >= LOLLIPOP 53 | ) { 54 | Shadow.applyOnAndroid(tnsView, Shadow.getDefaults(data)); 55 | } else if (tnsView.ios) { 56 | Shadow.applyOnIOS(tnsView, Shadow.getDefaults(data)); 57 | } 58 | } 59 | 60 | private static getDefaults(data: IOSData | AndroidData) { 61 | return Object.assign( 62 | {}, 63 | data, 64 | { 65 | shape: (data as AndroidData).shape || Shadow.DEFAULT_SHAPE, 66 | pressedElevation: (data as AndroidData).pressedElevation || Shadow.DEFAULT_PRESSED_ELEVATION, 67 | pressedTranslationZ: (data as AndroidData).pressedTranslationZ || Shadow.DEFAULT_PRESSED_ELEVATION, 68 | shadowColor: (data as IOSData).shadowColor || 69 | Shadow.DEFAULT_SHADOW_COLOR, 70 | useShadowPath: ((data as IOSData).useShadowPath !== undefined ? (data as IOSData).useShadowPath : true), 71 | rasterize: ((data as IOSData).rasterize !== undefined ? (data as IOSData).rasterize : false) 72 | }, 73 | ); 74 | } 75 | 76 | private static isShadow(drawable: any): boolean { 77 | return (drawable instanceof LayeredShadow || drawable instanceof PlainShadow); 78 | } 79 | 80 | private static applyOnAndroid(tnsView: any, data: AndroidData) { 81 | const nativeView = tnsView.android; 82 | let shape; 83 | let overrideBackground = true; 84 | 85 | 86 | let currentBg = nativeView.getBackground(); 87 | 88 | if (currentBg instanceof android.graphics.drawable.RippleDrawable) { // play nice if a ripple is wrapping a shadow 89 | let rippleBg = currentBg.getDrawable(0); 90 | if (rippleBg instanceof android.graphics.drawable.InsetDrawable) { 91 | overrideBackground = false; // this is a button with it's own shadow 92 | } else if (Shadow.isShadow(rippleBg)) { // if the ripple is wrapping a shadow, strip it 93 | currentBg = rippleBg; 94 | } 95 | } 96 | if (overrideBackground) { 97 | if (Shadow.isShadow(currentBg)) { // make sure to have the right background 98 | currentBg = currentBg instanceof LayeredShadow ? // if layered, get the original background 99 | currentBg.getDrawable(1) : null; 100 | } 101 | 102 | const outerRadii = Array.create("float", 8); 103 | if (data.cornerRadius === undefined) { 104 | outerRadii[0] = outerRadii[1] = Length.toDevicePixels(tnsView.borderTopLeftRadius, 0); 105 | outerRadii[2] = outerRadii[3] = Length.toDevicePixels(tnsView.borderTopRightRadius, 0); 106 | outerRadii[4] = outerRadii[5] = Length.toDevicePixels(tnsView.borderBottomRightRadius, 0); 107 | outerRadii[6] = outerRadii[7] = Length.toDevicePixels(tnsView.borderBottomLeftRadius, 0); 108 | } else { 109 | java.util.Arrays.fill(outerRadii, Shadow.androidDipToPx(nativeView, data.cornerRadius as number)); 110 | } 111 | 112 | // use the user defined color or the default in case the color is TRANSPARENT 113 | const bgColor = currentBg ? 114 | (currentBg instanceof android.graphics.drawable.ColorDrawable && currentBg.getColor() ? 115 | currentBg.getColor() : android.graphics.Color.parseColor(data.bgcolor || Shadow.DEFAULT_BGCOLOR)) : 116 | android.graphics.Color.parseColor(data.bgcolor || Shadow.DEFAULT_BGCOLOR); 117 | 118 | let newBg; 119 | 120 | if (data.shape !== ShapeEnum.RECTANGLE || data.bgcolor || !currentBg) { // replace background 121 | shape = new PlainShadow(); 122 | shape.setShape( 123 | android.graphics.drawable.GradientDrawable[data.shape as Shape], 124 | ); 125 | shape.setCornerRadii(outerRadii); 126 | shape.setColor(bgColor); 127 | newBg = shape; 128 | } else { // add a layer 129 | const r = new android.graphics.drawable.shapes.RoundRectShape(outerRadii, null, null); 130 | shape = new android.graphics.drawable.ShapeDrawable(r); 131 | shape.getPaint().setColor(bgColor); 132 | var arr = Array.create(android.graphics.drawable.Drawable, 2); 133 | arr[0] = shape; 134 | arr[1] = currentBg; 135 | const drawable = new LayeredShadow(arr); 136 | newBg = drawable; 137 | } 138 | 139 | nativeView.setBackgroundDrawable(newBg); 140 | } 141 | 142 | nativeView.setElevation( 143 | Shadow.androidDipToPx(nativeView, data.elevation as number), 144 | ); 145 | nativeView.setTranslationZ( 146 | Shadow.androidDipToPx(nativeView, data.translationZ as number), 147 | ); 148 | if (nativeView.getStateListAnimator() || data.forcePressAnimation) { 149 | this.overrideDefaultAnimator(nativeView, data); 150 | } 151 | } 152 | 153 | private static overrideDefaultAnimator(nativeView: any, data: AndroidData) { 154 | const sla = new android.animation.StateListAnimator(); 155 | 156 | const ObjectAnimator = android.animation.ObjectAnimator; 157 | const AnimatorSet = android.animation.AnimatorSet; 158 | const shortAnimTime = getAndroidR("integer", "config_shortAnimTime"); 159 | 160 | const buttonDuration = 161 | nativeView.getContext().getResources().getInteger(shortAnimTime) / 2; 162 | const pressedElevation = this.androidDipToPx(nativeView, data.pressedElevation as number); 163 | const pressedZ = this.androidDipToPx(nativeView, data.pressedTranslationZ as number); 164 | const elevation = this.androidDipToPx(nativeView, data.elevation); 165 | const z = this.androidDipToPx(nativeView, data.translationZ || 0); 166 | 167 | const pressedSet = new AnimatorSet(); 168 | const notPressedSet = new AnimatorSet(); 169 | const defaultSet = new AnimatorSet(); 170 | 171 | pressedSet.playTogether(java.util.Arrays.asList([ 172 | ObjectAnimator.ofFloat(nativeView, "translationZ", [pressedZ]) 173 | .setDuration(buttonDuration), 174 | ObjectAnimator.ofFloat(nativeView, "elevation", [pressedElevation]) 175 | .setDuration(0), 176 | ])); 177 | notPressedSet.playTogether(java.util.Arrays.asList([ 178 | ObjectAnimator.ofFloat(nativeView, "translationZ", [z]) 179 | .setDuration(buttonDuration), 180 | ObjectAnimator.ofFloat(nativeView, "elevation", [elevation]) 181 | .setDuration(0), 182 | ])); 183 | defaultSet.playTogether(java.util.Arrays.asList([ 184 | ObjectAnimator.ofFloat(nativeView, "translationZ", [0]).setDuration(0), 185 | ObjectAnimator.ofFloat(nativeView, "elevation", [0]).setDuration(0), 186 | ])); 187 | 188 | sla.addState( 189 | [getAndroidR("attr", "state_pressed"), getAndroidR("attr", "state_enabled")], 190 | pressedSet, 191 | ); 192 | sla.addState([getAndroidR("attr", "state_enabled")], notPressedSet); 193 | sla.addState([], defaultSet); 194 | nativeView.setStateListAnimator(sla); 195 | } 196 | 197 | private static applyOnIOS(tnsView: any, data: IOSData) { 198 | const nativeView = tnsView.ios; 199 | const elevation = parseFloat(((data.elevation as number) - 0).toFixed(2)); 200 | nativeView.layer.maskToBounds = false; 201 | nativeView.layer.shadowColor = new Color(data.shadowColor as string).ios.CGColor; 202 | nativeView.layer.shadowOffset = 203 | data.shadowOffset ? 204 | CGSizeMake(0, parseFloat(String(data.shadowOffset))) : 205 | CGSizeMake(0, 0.54 * elevation - 0.14); 206 | nativeView.layer.shadowOpacity = 207 | data.shadowOpacity ? 208 | parseFloat(String(data.shadowOpacity)) : 209 | 0.006 * elevation + 0.25; 210 | nativeView.layer.shadowRadius = 211 | data.shadowRadius ? 212 | parseFloat(String(data.shadowRadius)) : 213 | 0.66 * elevation - 0.5; 214 | nativeView.layer.shouldRasterize = data.rasterize; 215 | nativeView.layer.rasterizationScale = screen.mainScreen.scale; 216 | let shadowPath = null; 217 | if (data.useShadowPath) { 218 | shadowPath = UIBezierPath.bezierPathWithRoundedRectCornerRadius(nativeView.bounds, nativeView.layer.shadowRadius).cgPath; 219 | } 220 | nativeView.layer.shadowPath = shadowPath; 221 | } 222 | 223 | static androidDipToPx(nativeView: any, dip: number) { 224 | const metrics = nativeView.getContext().getResources().getDisplayMetrics(); 225 | return android.util.TypedValue.applyDimension( 226 | android.util.TypedValue.COMPLEX_UNIT_DIP, 227 | dip, 228 | metrics, 229 | ); 230 | } 231 | } -------------------------------------------------------------------------------- /src/common/shape.enum.ts: -------------------------------------------------------------------------------- 1 | export enum ShapeEnum { 2 | RECTANGLE = 'RECTANGLE', 3 | OVAL = 'OVAL', 4 | RING = 'RING', 5 | LINE = 'LINE', 6 | } 7 | 8 | export type Shape = 'RECTANGLE' | 'OVAL' | 'RING' | 'LINE'; 9 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { VueConstructor, PluginFunction } from 'vue'; 2 | 3 | // Import directive 4 | import { ShadowDirective } from './vue-shadow'; 5 | 6 | export function install(Vue: VueConstructor): void { 7 | 8 | if(install.installed) { 9 | console.log('not installed') 10 | return; 11 | } else { 12 | install.installed = true; 13 | Vue.directive('shadow', ShadowDirective); 14 | } 15 | }; 16 | 17 | class NSVueShadow { 18 | static install: PluginFunction; 19 | } 20 | 21 | export namespace install { 22 | export let installed: boolean; 23 | } 24 | 25 | NSVueShadow.install = install; 26 | 27 | // To auto-install when vue is found 28 | /* global window global */ 29 | let GlobalVue!: VueConstructor; 30 | if (typeof window !== "undefined" && typeof (window as any).Vue !== 'undefined') { 31 | GlobalVue = (window as any).Vue; 32 | } else if (typeof global !== "undefined" && typeof global['Vue'] !== 'undefined') { 33 | GlobalVue = global['Vue']; 34 | } 35 | if (GlobalVue) { 36 | GlobalVue.use(NSVueShadow); 37 | } 38 | 39 | export * from './common'; 40 | export default NSVueShadow; 41 | -------------------------------------------------------------------------------- /src/vue-shadow.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'nativescript-vue'; 2 | import { DirectiveOptions, VNodeDirective, VNode } from 'vue'; 3 | import { isAndroid, isIOS } from 'tns-core-modules/platform'; 4 | 5 | import { AndroidData } from './common/android-data.model'; 6 | import { IOSData } from './common/ios-data.model'; 7 | import { Shadow } from './common/shadow'; 8 | import { Shape } from './common/shape.enum'; 9 | import { View } from 'tns-core-modules/ui/page/page'; 10 | import { StackLayout } from 'tns-core-modules/ui/layouts/stack-layout'; 11 | import { addWeakEventListener, removeWeakEventListener } from "tns-core-modules/ui/core/weak-event-listener"; 12 | declare const android: any; 13 | 14 | export interface ShadowBindings extends VNodeDirective { 15 | value?: string | number | AndroidData | IOSData; 16 | } 17 | 18 | export class NativeShadowDirective { 19 | 20 | private el: any; 21 | private shadow!: string | number | AndroidData | IOSData; 22 | private elevation?: number | string; 23 | 24 | // along with this.elevation the following make up AndroidData object 25 | private pressedElevation?: number | string; 26 | private shape?: Shape; 27 | private bgcolor?: string; 28 | private cornerRadius?: number | string; 29 | private translationZ?: number | string; 30 | private pressedTranslationZ?: number | string; 31 | private forcePressAnimation?: boolean; 32 | 33 | // along with this.elevation the following make up IOSData object 34 | private maskToBounds?: boolean; 35 | private shadowColor?: string; 36 | private shadowOffset?: number | string; 37 | private shadowOpacity?: number | string; 38 | private shadowRadius?: number | string; 39 | private rasterize?: boolean; 40 | private useShadowPath?: boolean; 41 | 42 | // used to manage state 43 | private loaded = false; 44 | private initialized = false; 45 | private originalNSFn: any; 46 | private previousNSFn: any; 47 | private iosShadowWrapper!: View; 48 | private eventsBound = false; 49 | 50 | constructor(el: HTMLElement, binding: ShadowBindings) { 51 | this.el = el; 52 | 53 | if (binding.value && typeof binding.value !== 'object' && (typeof binding.value === 'string' || typeof binding.value === 'number') ) { 54 | this.shadow = binding.value; 55 | this.elevation = binding.value; 56 | } 57 | 58 | if (binding.value && typeof binding.value === 'object' && binding.value.elevation) { 59 | this.shadow = binding.value; 60 | this.elevation = this.shadow.elevation; 61 | if (isAndroid && ( 62 | ('pressedElevation' in this.shadow) || 63 | ('shape' in this.shadow) || 64 | ('bgcolor' in this.shadow) || 65 | ('cornerRadius' in this.shadow) || 66 | ('translationZ' in this.shadow) || 67 | ('pressedTranslationZ' in this.shadow) || 68 | ('forcePressAnimation' in this.shadow) 69 | )) { 70 | this.pressedElevation = this.shadow.pressedElevation; 71 | this.shape= this.shadow.shape; 72 | this.bgcolor = this.shadow.bgcolor; 73 | this.cornerRadius = this.shadow.cornerRadius; 74 | this.translationZ = this.shadow.translationZ; 75 | this.pressedTranslationZ = this.shadow.pressedTranslationZ; 76 | this.forcePressAnimation= this.shadow.forcePressAnimation; 77 | } else if (isIOS && ( 78 | ('maskToBounds' in this.shadow) || 79 | ('shadowColor' in this.shadow) || 80 | ('shadowOffset' in this.shadow) || 81 | ('shadowOpacity' in this.shadow) || 82 | ('shadowRadius' in this.shadow) || 83 | ('useShadowPath' in this.shadow) || 84 | ('rasterize' in this.shadow) 85 | )) { 86 | this.maskToBounds = this.shadow.maskToBounds; 87 | this.shadowColor = this.shadow.shadowColor; 88 | this.shadowOffset = this.shadow.shadowOffset; 89 | this.shadowOpacity = this.shadow.shadowOpacity; 90 | this.shadowRadius = this.shadow.shadowRadius; 91 | this.useShadowPath = this.shadow.useShadowPath; 92 | this.rasterize = this.shadow.rasterize; 93 | } else { 94 | // 95 | } 96 | } 97 | 98 | if (isAndroid) { 99 | if(this.el._nativeView._redrawNativeBackground) { 100 | this.originalNSFn = this.el._nativeView._redrawNativeBackground; //always store the original method 101 | } 102 | } 103 | } 104 | 105 | private initializeCommonData(): void { 106 | const tShadow = typeof this.shadow; 107 | if ((tShadow === 'string' || tShadow === 'number') && !this.elevation) { 108 | this.elevation = this.shadow ? parseInt(this.shadow as string, 10) : 2; 109 | } 110 | const tElevation = typeof this.elevation; 111 | if (tElevation === 'string' || tElevation === 'number') { 112 | this.elevation = this.elevation ? parseInt(this.elevation as string, 10) : 2; 113 | } 114 | } 115 | 116 | private initializeAndroidData(): void { 117 | if (typeof this.cornerRadius === 'string') { 118 | this.cornerRadius = parseInt(this.cornerRadius, 10); 119 | } 120 | if (typeof this.translationZ === 'string') { 121 | this.translationZ = parseInt(this.translationZ, 10); 122 | } 123 | } 124 | 125 | private initializeIOSData(): void { 126 | if (typeof this.shadowOffset === 'string') { 127 | this.shadowOffset = parseFloat(this.shadowOffset); 128 | } 129 | if (typeof this.shadowOpacity === 'string') { 130 | this.shadowOpacity = parseFloat(this.shadowOpacity); 131 | } 132 | if (typeof this.shadowRadius === 'string') { 133 | this.shadowRadius = parseFloat(this.shadowRadius); 134 | } 135 | } 136 | 137 | // Following sections (bindEvents & unbindEvents) of code is left in for historical purposes until more extensive testing 138 | // can be undertaken with the Vue version of the plugin. Commenting them out has no ill effects for Android, 139 | // but for IOS, the initial render has no shadows. If the v-shadow value is updated post render, then it will work 140 | 141 | // NS ListViews create elements dynamically 142 | // loaded and unloaded are called before angular is "ready" 143 | // https://github.com/NativeScript/nativescript-angular/issues/1221#issuecomment-422813111 144 | // So we ensure we're running loaded/unloaded events outside of angular 145 | private bindEvents(): void { 146 | if (!this.eventsBound) { 147 | addWeakEventListener(this.el._nativeView, View.loadedEvent, this.load, this); 148 | addWeakEventListener(this.el._nativeView, View.unloadedEvent, this.unload, this); 149 | this.eventsBound = true; 150 | // in some cases, the element is already loaded by time of binding 151 | if (this.el._nativeView.isLoaded) { 152 | this.load(); 153 | } 154 | } 155 | } 156 | 157 | private unbindEvents(): void { 158 | if (this.eventsBound) { 159 | removeWeakEventListener(this.el._nativeView, View.loadedEvent, this.load, this); 160 | removeWeakEventListener(this.el._nativeView, View.unloadedEvent, this.unload, this); 161 | this.eventsBound = false; 162 | } 163 | } 164 | 165 | private load(): void { 166 | this.loaded = true; 167 | 168 | this.applyShadow(); 169 | if (isAndroid) { 170 | this.previousNSFn = this.el._nativeView._redrawNativeBackground; // just to maintain compatibility with other patches 171 | this.el._nativeView._redrawNativeBackground = this.monkeyPatch; 172 | } 173 | } 174 | 175 | private unload(): void { 176 | this.loaded = false; 177 | 178 | if (isAndroid) { 179 | this.el._nativeView._redrawNativeBackground = this.originalNSFn; // always revert to the original method 180 | } 181 | } 182 | 183 | private monkeyPatch = (val): void => { 184 | this.previousNSFn.call(this.el._nativeView, val); 185 | this.applyShadow(); 186 | }; 187 | 188 | private applyShadow(): void { 189 | if (!this.shadow && !this.elevation) { 190 | return; 191 | } 192 | 193 | // For shadows to be shown on Android the SDK has to be greater 194 | // or equal than 21, lower SDK means no setElevation method is available 195 | if (isAndroid) { 196 | if (android.os.Build.VERSION.SDK_INT < 21) { 197 | return; 198 | } 199 | } 200 | 201 | const viewToApplyShadowTo = isIOS ? this.iosShadowWrapper : this.el._nativeView; 202 | 203 | if (viewToApplyShadowTo) { 204 | Shadow.apply(viewToApplyShadowTo, { 205 | elevation: this.elevation as number, 206 | 207 | // along with this.elevation the following make up AndroidData object 208 | pressedElevation: this.pressedElevation as number, 209 | shape: this.shape, 210 | bgcolor: this.bgcolor, 211 | cornerRadius: this.cornerRadius, 212 | translationZ: this.translationZ, 213 | pressedTranslationZ: this.pressedTranslationZ, 214 | forcePressAnimation: this.forcePressAnimation, 215 | 216 | // along with this.elevation the following make up IOSData object 217 | maskToBounds: this.maskToBounds, 218 | shadowColor: this.shadowColor, 219 | shadowOffset: this.shadowOffset as number, 220 | shadowOpacity: this.shadowOpacity as number, 221 | shadowRadius: this.shadowRadius as number, 222 | rasterize: this.rasterize, 223 | useShadowPath: this.useShadowPath 224 | }); 225 | } 226 | } 227 | 228 | private loadFromAndroidData(data: AndroidData): void { 229 | this.elevation = data.elevation || this.elevation; 230 | this.shape = data.shape || this.shape; 231 | this.bgcolor = data.bgcolor || this.bgcolor; 232 | this.cornerRadius = data.cornerRadius || this.cornerRadius; 233 | this.translationZ = data.translationZ || this.translationZ; 234 | this.pressedTranslationZ = data.pressedTranslationZ || this.pressedTranslationZ; 235 | this.forcePressAnimation = data.forcePressAnimation || this.forcePressAnimation; 236 | } 237 | 238 | private loadFromIOSData(data: IOSData): void { 239 | this.maskToBounds = data.maskToBounds || this.maskToBounds; 240 | this.shadowColor = data.shadowColor || this.shadowColor; 241 | this.shadowOffset = data.shadowOffset || this.shadowOffset; 242 | this.shadowOpacity = data.shadowOpacity || this.shadowOpacity; 243 | this.shadowRadius = data.shadowRadius || this.shadowRadius; 244 | this.rasterize = data.rasterize || this.rasterize; 245 | this.useShadowPath = data.useShadowPath || this.useShadowPath; 246 | } 247 | 248 | public init(): void { // RadListView calls this multiple times for the same view 249 | if (!this.initialized) { 250 | this.initialized = true; 251 | this.initializeCommonData(); 252 | if (isAndroid) { 253 | this.initializeAndroidData(); 254 | } else if (isIOS) { 255 | this.initializeIOSData(); 256 | } 257 | if (this.shadow && (this.shadow as AndroidData | IOSData).elevation) { 258 | if (isAndroid) { 259 | this.loadFromAndroidData(this.shadow as AndroidData); 260 | } else if (isIOS) { 261 | this.loadFromIOSData(this.shadow as IOSData); 262 | } 263 | } 264 | 265 | if (!this.shadow && this.elevation) { 266 | if (isAndroid) { 267 | this.loadFromAndroidData({ 268 | elevation: this.elevation, 269 | pressedElevation: this.pressedElevation, 270 | shape: this.shape, 271 | bgcolor: this.bgcolor, 272 | cornerRadius: this.cornerRadius, 273 | translationZ: this.translationZ, 274 | pressedTranslationZ: this.pressedTranslationZ, 275 | forcePressAnimation: this.forcePressAnimation, 276 | } as AndroidData); 277 | } else if (isIOS) { 278 | this.loadFromIOSData({ 279 | elevation: this.elevation, 280 | maskToBounds: this.maskToBounds, 281 | shadowColor: this.shadowColor, 282 | shadowOffset: this.shadowOffset as number, 283 | shadowOpacity: this.shadowOpacity as number, 284 | shadowRadius: this.shadowRadius as number, 285 | rasterize: this.rasterize, 286 | useShadowPath: this.useShadowPath 287 | } as IOSData); 288 | } 289 | } 290 | 291 | this.bindEvents(); 292 | } 293 | } 294 | 295 | public addIOSWrapper(): void { 296 | if (isIOS) { 297 | const originalElement = this.el; 298 | const parent = originalElement.parentNode; 299 | 300 | const vm = new Vue({ 301 | template: '', 302 | }).$mount(); 303 | 304 | // @ts-ignore 305 | const wrapper = vm.$el; 306 | 307 | parent.insertBefore(wrapper, originalElement); 308 | parent.removeChild(originalElement); 309 | wrapper.appendChild(originalElement); 310 | 311 | // @ts-ignore 312 | this.iosShadowWrapper = wrapper._nativeView as StackLayout; 313 | } 314 | } 315 | 316 | public onUpdate(values: string | number | AndroidData | IOSData) { 317 | if (this.loaded && !!values) { 318 | if (typeof values !== 'object' && (typeof values === 'string' || typeof values === 'number')) { 319 | this.shadow = values; 320 | this.elevation = values; 321 | } 322 | if (typeof values === 'object' && values.elevation) { 323 | this.shadow = values; 324 | this.elevation = this.shadow.elevation; 325 | if (isAndroid) { 326 | this.loadFromAndroidData(this.shadow as AndroidData); 327 | } else if (isIOS) { 328 | this.loadFromIOSData(this.shadow as IOSData); 329 | } 330 | } 331 | 332 | this.applyShadow(); 333 | } 334 | } 335 | 336 | public destroy(): void { 337 | if (this.initialized) { 338 | this.unload(); 339 | this.unbindEvents(); 340 | this.initialized = false; 341 | } 342 | } 343 | } 344 | 345 | export const ShadowDirective: DirectiveOptions = { 346 | 347 | bind(el: HTMLElement, binding: ShadowBindings, vnode: VNode) { 348 | // console.log("v-shadow - bind") 349 | const shadowDir: NativeShadowDirective = new NativeShadowDirective(el, binding); 350 | shadowDir.init(); 351 | // @ts-ignore 352 | el.__vShadow = shadowDir; 353 | }, 354 | inserted(el: HTMLElement, binding: ShadowBindings, vnode: VNode) { 355 | // console.log("v-shadow - inserted") 356 | // @ts-ignore 357 | const shadowDir = el.__vShadow 358 | shadowDir.addIOSWrapper(); 359 | }, 360 | update(el: HTMLElement, { value }, vnode: VNode) { 361 | // console.log("v-shadow - update") 362 | // console.log("v-shadow - update - value - ", value); 363 | // @ts-ignore 364 | const shadowDir = el.__vShadow 365 | shadowDir.onUpdate(value as string | number | AndroidData | IOSData); 366 | }, 367 | unbind(el: HTMLElement, binding: ShadowBindings, vnode: VNode) { 368 | // console.log("v-shadow - unbind") 369 | // @ts-ignore 370 | const shadowDir = el.__vShadow 371 | shadowDir.destroy(); 372 | // @ts-ignore 373 | el.__vShadow = null; 374 | 375 | }, 376 | }; -------------------------------------------------------------------------------- /tests/unit/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | jest: true 4 | } 5 | }; 6 | -------------------------------------------------------------------------------- /tests/unit/example.spec.ts: -------------------------------------------------------------------------------- 1 | import { shallowMount } from "@vue/test-utils"; 2 | import HelloWorld from "@/components/HelloWorld.vue"; 3 | 4 | describe("HelloWorld.vue", () => { 5 | it("renders props.msg when passed", () => { 6 | const msg = "new message"; 7 | const wrapper = shallowMount(HelloWorld, { 8 | propsData: { msg } 9 | }); 10 | expect(wrapper.text()).toMatch(msg); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /tests/unit/plugin.spec.ts: -------------------------------------------------------------------------------- 1 | // import { shallowMount, createLocalVue } from '@vue/test-utils' 2 | // // import HelloWorld from '@/components/HelloWorld.vue' 3 | // import plugin from '../../src/entry' 4 | // // import { PluginAddFunction } from '../../types/dyanmo' 5 | 6 | // // describe('Plugin', () => { 7 | // // it('should be 2', () => { 8 | // // const msg = 'new message' 9 | // // const localVue = createLocalVue() 10 | // // localVue.use(plugin) 11 | // // const wrapper = shallowMount(HelloWorld, { 12 | // // localVue, 13 | // // propsData: { msg } 14 | // // }) 15 | // // expect(wrapper.vm.$add(1, 1)).toBe(2) 16 | // // }) 17 | // // }) 18 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "outDir": "dist", 5 | "module": "esnext", 6 | "strict": true, 7 | "jsx": "preserve", 8 | "importHelpers": true, 9 | "moduleResolution": "node", 10 | "experimentalDecorators": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "sourceMap": true, 14 | "baseUrl": ".", 15 | "resolveJsonModule": true, 16 | "declaration": true, 17 | "declarationDir": "types", 18 | "allowJs": false, 19 | "strictNullChecks": true, 20 | "noImplicitAny": false, 21 | "removeComments": true, 22 | "types": [ 23 | "node", 24 | "jest" 25 | ], 26 | "paths": { 27 | "@/*": [ 28 | "src/*" 29 | ] 30 | }, 31 | "lib": [ 32 | "esnext", 33 | "dom", 34 | "dom.iterable", 35 | "scripthost" 36 | ] 37 | }, 38 | "include": [ 39 | "src/**/*", 40 | "types/vue-shims.d.ts" 41 | ], 42 | "exclude": [ 43 | "node_modules", 44 | "dist", 45 | "demo" 46 | ] 47 | } 48 | -------------------------------------------------------------------------------- /types/common/android-data.model.d.ts: -------------------------------------------------------------------------------- 1 | import { Shape } from './shape.enum'; 2 | export declare type AndroidData = { 3 | elevation: number; 4 | pressedElevation?: number; 5 | shape?: Shape; 6 | bgcolor?: string; 7 | cornerRadius?: number; 8 | translationZ?: number; 9 | pressedTranslationZ?: number; 10 | forcePressAnimation?: boolean; 11 | }; 12 | -------------------------------------------------------------------------------- /types/common/elevation.enum.d.ts: -------------------------------------------------------------------------------- 1 | export declare enum Elevation { 2 | SWITCH = 1, 3 | CARD_RESTING = 2, 4 | RAISED_BUTTON_RESTING = 2, 5 | SEARCH_BAR_RESTING = 2, 6 | REFRESH_INDICADOR = 3, 7 | SEARCH_BAR_SCROLLED = 3, 8 | APPBAR = 4, 9 | FAB_RESTING = 6, 10 | SNACKBAR = 6, 11 | BOTTOM_NAVIGATION_BAR = 8, 12 | MENU = 8, 13 | CARD_PICKED_UP = 8, 14 | RAISED_BUTTON_PRESSED = 8, 15 | SUBMENU_LEVEL1 = 9, 16 | SUBMENU_LEVEL2 = 10, 17 | SUBMENU_LEVEL3 = 11, 18 | SUBMENU_LEVEL4 = 12, 19 | SUBMENU_LEVEL5 = 13, 20 | FAB_PRESSED = 12, 21 | NAV_DRAWER = 16, 22 | RIGHT_DRAWER = 16, 23 | MODAL_BOTTOM_SHEET = 16, 24 | DIALOG = 24, 25 | PICKER = 24 26 | } 27 | -------------------------------------------------------------------------------- /types/common/index.d.ts: -------------------------------------------------------------------------------- 1 | export * from './android-data.model'; 2 | export * from './elevation.enum'; 3 | export * from './ios-data.model'; 4 | export * from './shadow'; 5 | export * from './shape.enum'; 6 | -------------------------------------------------------------------------------- /types/common/ios-data.model.d.ts: -------------------------------------------------------------------------------- 1 | export declare type IOSData = { 2 | elevation: number; 3 | maskToBounds?: boolean; 4 | shadowColor?: string; 5 | shadowOffset?: number; 6 | shadowOpacity?: number; 7 | shadowRadius?: number; 8 | rasterize?: boolean; 9 | useShadowPath?: boolean; 10 | }; 11 | -------------------------------------------------------------------------------- /types/common/shadow.d.ts: -------------------------------------------------------------------------------- 1 | import { AndroidData } from "./android-data.model"; 2 | import { IOSData } from "./ios-data.model"; 3 | import { ShapeEnum } from './shape.enum'; 4 | export declare class Shadow { 5 | static DEFAULT_SHAPE: ShapeEnum; 6 | static DEFAULT_BGCOLOR: string; 7 | static DEFAULT_SHADOW_COLOR: string; 8 | static DEFAULT_PRESSED_ELEVATION: number; 9 | static DEFAULT_PRESSED_Z: number; 10 | static apply(tnsView: any, data: IOSData | AndroidData): void; 11 | private static getDefaults; 12 | private static isShadow; 13 | private static applyOnAndroid; 14 | private static overrideDefaultAnimator; 15 | private static applyOnIOS; 16 | static androidDipToPx(nativeView: any, dip: number): any; 17 | } 18 | -------------------------------------------------------------------------------- /types/common/shape.enum.d.ts: -------------------------------------------------------------------------------- 1 | export declare enum ShapeEnum { 2 | RECTANGLE = "RECTANGLE", 3 | OVAL = "OVAL", 4 | RING = "RING", 5 | LINE = "LINE" 6 | } 7 | export declare type Shape = 'RECTANGLE' | 'OVAL' | 'RING' | 'LINE'; 8 | -------------------------------------------------------------------------------- /types/index.d.ts: -------------------------------------------------------------------------------- 1 | import { VueConstructor, PluginFunction } from 'vue'; 2 | export declare function install(Vue: VueConstructor): void; 3 | declare class NSVueShadow { 4 | static install: PluginFunction; 5 | } 6 | export declare namespace install { 7 | let installed: boolean; 8 | } 9 | export * from './common'; 10 | export default NSVueShadow; 11 | -------------------------------------------------------------------------------- /types/vue-shadow.d.ts: -------------------------------------------------------------------------------- 1 | import { DirectiveOptions, VNodeDirective } from 'vue'; 2 | import { AndroidData } from './common/android-data.model'; 3 | import { IOSData } from './common/ios-data.model'; 4 | export interface ShadowBindings extends VNodeDirective { 5 | value?: string | number | AndroidData | IOSData; 6 | } 7 | export declare class NativeShadowDirective { 8 | private el; 9 | private shadow; 10 | private elevation?; 11 | private pressedElevation?; 12 | private shape?; 13 | private bgcolor?; 14 | private cornerRadius?; 15 | private translationZ?; 16 | private pressedTranslationZ?; 17 | private forcePressAnimation?; 18 | private maskToBounds?; 19 | private shadowColor?; 20 | private shadowOffset?; 21 | private shadowOpacity?; 22 | private shadowRadius?; 23 | private rasterize?; 24 | private useShadowPath?; 25 | private loaded; 26 | private initialized; 27 | private originalNSFn; 28 | private previousNSFn; 29 | private iosShadowWrapper; 30 | private eventsBound; 31 | constructor(el: HTMLElement, binding: ShadowBindings); 32 | private initializeCommonData; 33 | private initializeAndroidData; 34 | private initializeIOSData; 35 | private bindEvents; 36 | private unbindEvents; 37 | private load; 38 | private unload; 39 | private monkeyPatch; 40 | private applyShadow; 41 | private loadFromAndroidData; 42 | private loadFromIOSData; 43 | init(): void; 44 | addIOSWrapper(): void; 45 | onUpdate(values: string | number | AndroidData | IOSData): void; 46 | destroy(): void; 47 | } 48 | export declare const ShadowDirective: DirectiveOptions; 49 | -------------------------------------------------------------------------------- /types/vue-shims.d.ts: -------------------------------------------------------------------------------- 1 | 2 | declare module "*.vue" { 3 | import Vue from "vue"; 4 | export default Vue; 5 | } --------------------------------------------------------------------------------