├── .browserslistrc ├── .eslintignore ├── .eslintrc.js ├── .github └── workflows │ ├── ci.yml │ └── deploy.yml ├── .gitignore ├── .husky ├── commit-msg └── pre-commit ├── .stylelintignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── babel.config.js ├── commitlint.config.js ├── docs ├── app.vue ├── components │ └── demo-block.vue ├── index.html ├── index.ts └── shims.d.ts ├── jest.config.js ├── lint-staged.config.js ├── package-lock.json ├── package.json ├── rollup.config.js ├── src ├── README.md ├── index.ts └── shims.d.ts ├── stylelint.config.js ├── tests ├── events.spec.ts └── props.spec.ts ├── tsconfig.eslint.json ├── tsconfig.json └── webpack.config.js /.browserslistrc: -------------------------------------------------------------------------------- 1 | defaults 2 | not ie 11 3 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | *.local* 2 | coverage 3 | dist 4 | node_modules 5 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | browser: true, 5 | node: true, 6 | }, 7 | extends: [ 8 | 'airbnb-base', 9 | 'airbnb-typescript/base', 10 | 'plugin:@typescript-eslint/recommended', 11 | 'plugin:vue/vue3-recommended', 12 | ], 13 | parser: 'vue-eslint-parser', 14 | parserOptions: { 15 | parser: '@typescript-eslint/parser', 16 | project: 'tsconfig.eslint.json', 17 | sourceType: 'module', 18 | extraFileExtensions: ['.vue'], 19 | }, 20 | plugins: [ 21 | '@typescript-eslint', 22 | 'import', 23 | 'vue', 24 | ], 25 | rules: { 26 | '@typescript-eslint/no-explicit-any': 'off', 27 | '@typescript-eslint/no-var-requires': 'off', 28 | 'no-restricted-properties': 'off', 29 | }, 30 | overrides: [ 31 | { 32 | files: ['tests/**/*.ts'], 33 | env: { 34 | jest: true, 35 | }, 36 | }, 37 | ], 38 | }; 39 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: [main] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v2 15 | - uses: actions/setup-node@v2 16 | with: 17 | node-version: 16 18 | - run: npm install 19 | - run: npm run lint 20 | - run: npm run build 21 | - run: npm test 22 | - run: npm run test:coverage 23 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy 2 | 3 | on: 4 | push: 5 | tags: 6 | - v2.* 7 | workflow_dispatch: 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v2 15 | - uses: actions/setup-node@v2 16 | with: 17 | node-version: 16 18 | - run: npm install 19 | - run: npm run build:docs 20 | - run: | 21 | cd docs/dist 22 | git init 23 | git config user.name "${{ github.actor }}" 24 | git config user.email "${{ github.actor }}@users.noreply.github.com" 25 | git add --all 26 | git commit --message "♥" 27 | git push --force https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git master:gh-pages 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.local* 2 | *.log 3 | *.map 4 | .DS_Store 5 | coverage 6 | dist 7 | node_modules 8 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx commitlint --edit $1 5 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx lint-staged 5 | -------------------------------------------------------------------------------- /.stylelintignore: -------------------------------------------------------------------------------- 1 | *.local* 2 | coverage 3 | dist 4 | node_modules 5 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # [2.0.0](https://github.com/fengyuanchen/vue-qrcode/compare/v2.0.0-rc.1...v2.0.0) (2022-02-07) 2 | 3 | 4 | 5 | # [2.0.0-rc.1](https://github.com/fengyuanchen/vue-qrcode/compare/v2.0.0-rc...v2.0.0-rc.1) (2021-08-23) 6 | 7 | 8 | ### Bug Fixes 9 | 10 | * improve the compatibility of svg elements ([102a388](https://github.com/fengyuanchen/vue-qrcode/commit/102a38859e4d6211905981faf13c3b915246fa3c)) 11 | 12 | 13 | ### Features 14 | 15 | * add `ready` event ([10ddebb](https://github.com/fengyuanchen/vue-qrcode/commit/10ddebba22b731348364ede873947ac3b4d33eaa)) 16 | 17 | 18 | 19 | # [2.0.0-rc](https://github.com/fengyuanchen/vue-qrcode/compare/v2.0.0-beta...v2.0.0-rc) (2021-06-12) 20 | 21 | 22 | 23 | # [2.0.0-beta](https://github.com/fengyuanchen/vue-qrcode/compare/v2.0.0-alpha...v2.0.0-beta) (2021-02-10) 24 | 25 | 26 | 27 | # [2.0.0-alpha](https://github.com/fengyuanchen/vue-qrcode/compare/v1.0.2...v2.0.0-alpha) (2021-01-30) 28 | 29 | 30 | * refactor!: upgrade to Vue 3 ([14c1866](https://github.com/fengyuanchen/vue-qrcode/commit/14c1866d88117f1f895a6b90407f62d50d18a5d1)) 31 | 32 | 33 | ### BREAKING CHANGES 34 | 35 | * drop support for Vue 2. 36 | 37 | 38 | 39 | ## [1.0.2](https://github.com/fengyuanchen/vue-qrcode/compare/v1.0.1...v1.0.2) (2020-01-18) 40 | 41 | 42 | 43 | ## [1.0.1](https://github.com/fengyuanchen/vue-qrcode/compare/v1.0.0...v1.0.1) (2019-06-29) 44 | 45 | 46 | 47 | # 1.0.0 (2018-10-21) 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright 2018-present Chen Fengyuan 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 13 | all 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 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue-qrcode 2 | 3 | [![Coverage Status](https://img.shields.io/codecov/c/github/fengyuanchen/vue-qrcode.svg)](https://codecov.io/gh/fengyuanchen/vue-qrcode) [![Downloads](https://img.shields.io/npm/dm/@chenfengyuan/vue-qrcode.svg)](https://www.npmjs.com/package/@chenfengyuan/vue-qrcode) [![Version](https://img.shields.io/npm/v/@chenfengyuan/vue-qrcode.svg)](https://www.npmjs.com/package/@chenfengyuan/vue-qrcode) [![Gzip Size](https://img.shields.io/bundlephobia/minzip/@chenfengyuan/vue-qrcode.svg)](https://unpkg.com/@chenfengyuan/vue-qrcode/dist/vue-qrcode.js) 4 | 5 | > QR code component for Vue 3, bases on [node-qrcode](https://github.com/soldair/node-qrcode). For Vue 2, check out the [`v1`](https://github.com/fengyuanchen/vue-qrcode/tree/v1) branch. 6 | 7 | - [Docs](src/README.md) 8 | - [Demo](https://fengyuanchen.github.io/vue-qrcode) 9 | 10 | ## Main npm package files 11 | 12 | ```text 13 | dist/ 14 | ├── vue-qrcode.js (UMD, default) 15 | ├── vue-qrcode.min.js (UMD, compressed) 16 | ├── vue-qrcode.esm.js (ECMAScript Module) 17 | ├── vue-qrcode.esm.min.js (ECMAScript Module, compressed) 18 | └── vue-qrcode.d.ts (TypeScript Declaration File) 19 | ``` 20 | 21 | ## Getting started 22 | 23 | ### Installation 24 | 25 | Using npm: 26 | 27 | ```shell 28 | npm install vue@3 qrcode@1 @chenfengyuan/vue-qrcode@2 29 | ``` 30 | 31 | Using pnpm: 32 | 33 | ```shell 34 | pnpm add vue@3 qrcode@1 @chenfengyuan/vue-qrcode@2 35 | ``` 36 | 37 | Using Yarn: 38 | 39 | ```shell 40 | yarn add vue@3 qrcode@1 @chenfengyuan/vue-qrcode@2 41 | ``` 42 | 43 | Using CDN: 44 | 45 | ```html 46 | 47 | 48 | 49 | ``` 50 | 51 | ### Usage 52 | 53 | ```js 54 | import { createApp } from 'vue'; 55 | import VueQrcode from '@chenfengyuan/vue-qrcode'; 56 | 57 | const app = createApp({}); 58 | 59 | app.component(VueQrcode.name, VueQrcode); 60 | ``` 61 | 62 | ```html 63 | 64 | ``` 65 | 66 | ## Browser support 67 | 68 | Same as Vue 3. 69 | 70 | ## Versioning 71 | 72 | Maintained under the [Semantic Versioning guidelines](https://semver.org/). 73 | 74 | ## License 75 | 76 | [MIT](https://opensource.org/licenses/MIT) © [Chen Fengyuan](https://chenfengyuan.com/) 77 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['@babel/preset-env'], 3 | }; 4 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: [ 3 | '@commitlint/config-conventional', 4 | ], 5 | }; 6 | -------------------------------------------------------------------------------- /docs/app.vue: -------------------------------------------------------------------------------- 1 | 115 | 116 | 143 | 144 | 244 | -------------------------------------------------------------------------------- /docs/components/demo-block.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 14 | 15 | 33 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | vue-qrcode 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /docs/index.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue'; 2 | import App from './app.vue'; 3 | import DemoBlock from './components/demo-block.vue'; 4 | import VueQrcode from '../src'; 5 | 6 | const app = createApp(App); 7 | 8 | app.component(DemoBlock.name, DemoBlock); 9 | app.component(VueQrcode.name, VueQrcode); 10 | app.mount('#app'); 11 | -------------------------------------------------------------------------------- /docs/shims.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.vue' { 2 | const content: any; 3 | 4 | export default content; 5 | } 6 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'ts-jest', 3 | collectCoverage: true, 4 | coverageDirectory: 'coverage', 5 | coverageReporters: ['html', 'lcov', 'text'], 6 | moduleFileExtensions: ['js', 'ts'], 7 | transform: { 8 | '^.+\\.js$': 'babel-jest', 9 | }, 10 | testEnvironment: 'jsdom', 11 | testMatch: ['**/tests/*.spec.ts'], 12 | }; 13 | -------------------------------------------------------------------------------- /lint-staged.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | '*.{js,ts,vue}': 'eslint --fix', 3 | '*.{css,scss,vue}': 'stylelint --fix', 4 | }; 5 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@chenfengyuan/vue-qrcode", 3 | "version": "2.0.0", 4 | "description": "QR code component for Vue 3.", 5 | "main": "dist/vue-qrcode.js", 6 | "module": "dist/vue-qrcode.esm.js", 7 | "types": "dist/vue-qrcode.d.ts", 8 | "files": [ 9 | "dist" 10 | ], 11 | "scripts": { 12 | "build": "rollup -c --environment BUILD:production", 13 | "build:docs": "webpack --env production", 14 | "build:types": "move-file dist/index.d.ts dist/vue-qrcode.d.ts", 15 | "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0", 16 | "clean": "del-cli dist", 17 | "lint": "npm run lint:js && npm run lint:css", 18 | "lint:css": "stylelint **/*.{css,scss,vue} --fix", 19 | "lint:js": "eslint . --ext .js,.ts,.vue --fix", 20 | "prepare": "husky install", 21 | "release": "npm run clean && npm run lint && npm run build && npm run build:types && npm run build:docs && npm test && npm run changelog", 22 | "serve": "webpack serve --hot --open", 23 | "start": "npm run serve", 24 | "test": "jest", 25 | "test:coverage": "cat coverage/lcov.info | codecov" 26 | }, 27 | "repository": { 28 | "type": "git", 29 | "url": "https://github.com/fengyuanchen/vue-qrcode.git" 30 | }, 31 | "keywords": [ 32 | "qrcode", 33 | "qr-code", 34 | "2d-bar-code", 35 | "vue", 36 | "vue3", 37 | "vue-component", 38 | "front-end", 39 | "web" 40 | ], 41 | "author": "Chen Fengyuan (https://chenfengyuan.com/)", 42 | "license": "MIT", 43 | "bugs": "https://github.com/fengyuanchen/vue-qrcode/issues", 44 | "homepage": "https://fengyuanchen.github.io/vue-qrcode", 45 | "devDependencies": { 46 | "@babel/core": "^7.17.0", 47 | "@babel/preset-env": "^7.16.11", 48 | "@commitlint/cli": "^16.1.0", 49 | "@commitlint/config-conventional": "^16.0.0", 50 | "@types/jest": "^27.4.0", 51 | "@types/qrcode": "^1.4.2", 52 | "@typescript-eslint/eslint-plugin": "^5.10.2", 53 | "@typescript-eslint/parser": "^5.10.2", 54 | "@vue/test-utils": "^2.0.0-rc.18", 55 | "babel-jest": "^27.5.0", 56 | "babel-loader": "^8.2.3", 57 | "canvas": "^2.9.0", 58 | "change-case": "^4.1.2", 59 | "codecov": "^3.8.3", 60 | "conventional-changelog-cli": "^2.2.2", 61 | "create-banner": "^2.0.0", 62 | "css-loader": "^6.6.0", 63 | "del-cli": "^4.0.1", 64 | "eslint": "^8.8.0", 65 | "eslint-config-airbnb-base": "^15.0.0", 66 | "eslint-config-airbnb-typescript": "^16.1.0", 67 | "eslint-plugin-import": "^2.25.4", 68 | "eslint-plugin-vue": "^8.4.1", 69 | "html-webpack-plugin": "^5.5.0", 70 | "husky": "^7.0.4", 71 | "jest": "^27.5.0", 72 | "lint-staged": "^12.3.3", 73 | "markdown-it-anchor": "^8.4.1", 74 | "markdown-to-vue-loader": "^3.1.3", 75 | "mini-css-extract-plugin": "^2.5.3", 76 | "move-file-cli": "^3.0.0", 77 | "qrcode": "^1.5.0", 78 | "rollup": "^2.67.1", 79 | "rollup-plugin-terser": "^7.0.2", 80 | "rollup-plugin-typescript2": "^0.31.2", 81 | "sass": "^1.49.7", 82 | "sass-loader": "^12.4.0", 83 | "style-loader": "^3.3.1", 84 | "stylelint": "^14.3.0", 85 | "stylelint-config-recommended-scss": "^5.0.2", 86 | "stylelint-config-recommended-vue": "^1.1.0", 87 | "stylelint-order": "^5.0.0", 88 | "ts-jest": "^27.1.3", 89 | "ts-loader": "^9.2.6", 90 | "tslib": "^2.3.1", 91 | "typescript": "^4.5.5", 92 | "vue": "^3.2.30", 93 | "vue-loader": "^17.0.0", 94 | "webpack": "^5.68.0", 95 | "webpack-cli": "^4.9.2", 96 | "webpack-dev-server": "^4.7.4" 97 | }, 98 | "peerDependencies": { 99 | "qrcode": "^1.5.0", 100 | "vue": "^3.0.0" 101 | }, 102 | "publishConfig": { 103 | "access": "public" 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import createBanner from 'create-banner'; 2 | import typescript from 'rollup-plugin-typescript2'; 3 | import { pascalCase } from 'change-case'; 4 | import { terser } from 'rollup-plugin-terser'; 5 | import pkg from './package.json'; 6 | 7 | const name = pascalCase(pkg.name.replace(/^.+\//, '')); 8 | const banner = createBanner({ 9 | data: { 10 | year: '2018-present', 11 | }, 12 | template: 'inline', 13 | }); 14 | 15 | export default ['umd', 'esm'].map((format) => ({ 16 | input: 'src/index.ts', 17 | output: ['development', 'production'].map((mode) => { 18 | const output = { 19 | banner, 20 | format, 21 | name, 22 | file: pkg.main, 23 | globals: { 24 | vue: 'Vue', 25 | qrcode: 'QRCode', 26 | }, 27 | }; 28 | 29 | if (format === 'esm') { 30 | output.file = pkg.module; 31 | } 32 | 33 | if (mode === 'production') { 34 | output.compact = true; 35 | output.file = output.file.replace(/(\.js)$/, '.min$1'); 36 | output.plugins = [ 37 | terser(), 38 | ]; 39 | } 40 | 41 | return output; 42 | }), 43 | external: Object.keys(pkg.peerDependencies), 44 | plugins: [ 45 | typescript({ 46 | tsconfigOverride: { 47 | compilerOptions: { 48 | declaration: format === 'esm', 49 | }, 50 | exclude: [ 51 | 'docs', 52 | 'tests', 53 | ], 54 | }, 55 | }), 56 | ], 57 | })); 58 | -------------------------------------------------------------------------------- /src/README.md: -------------------------------------------------------------------------------- 1 | # QR Code 2 | 3 | > Easy to use QR code component, bases on [node-qrcode](https://github.com/soldair/node-qrcode). 4 | 5 | ## Basic usage 6 | 7 | ```html 8 | 12 | ``` 13 | 14 | ## Add options 15 | 16 | ```html 17 | 29 | ``` 30 | 31 | ## Custom tag 32 | 33 | ```html 34 | 38 | ``` 39 | 40 | ## Add a logo 41 | 42 | ### Covering 43 | 44 | ```html 45 | 62 | 63 | 85 | ``` 86 | 87 | ### Drawing 88 | 89 | > Only available to canvas elements. 90 | 91 | ```html 92 | 102 | 103 | 144 | ``` 145 | 146 | ## Props 147 | 148 | | Name | Type | Default | Options | Description | 149 | | --- | --- | --- | --- | --- | 150 | | value | `string` | - | - | The value of the QR code. | 151 | | options | `Object` | - | [Checkout the available options](https://github.com/soldair/node-qrcode#qr-code-options) | The options for the QR code generator. | 152 | | tag | `string` | `"canvas"` | canvas, img, svg | The tag of the QR code. | 153 | 154 | ## Events 155 | 156 | | Name | Parameters | Description | 157 | | --- | --- | --- | 158 | | ready | `($el)` | Fire when the QR code is generated. | 159 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { defineComponent, h } from 'vue'; 2 | import { toCanvas, toDataURL, toString } from 'qrcode'; 3 | 4 | const EVENT_READY = 'ready'; 5 | 6 | export default defineComponent({ 7 | name: 'VueQrcode', 8 | 9 | props: { 10 | /** 11 | * The value of the QR code. 12 | */ 13 | value: { 14 | type: String, 15 | default: undefined, 16 | }, 17 | 18 | /** 19 | * The options for the QR code generator. 20 | * {@link https://github.com/soldair/node-qrcode#qr-code-options} 21 | */ 22 | options: { 23 | type: Object, 24 | default: undefined, 25 | }, 26 | 27 | /** 28 | * The tag name of the component's root element. 29 | */ 30 | tag: { 31 | type: String, 32 | default: 'canvas', 33 | }, 34 | }, 35 | 36 | emits: [EVENT_READY], 37 | 38 | watch: { 39 | $props: { 40 | deep: true, 41 | immediate: true, 42 | 43 | /** 44 | * Update the QR code when props changed. 45 | */ 46 | handler() { 47 | if (this.$el) { 48 | this.generate(); 49 | } 50 | }, 51 | }, 52 | }, 53 | 54 | mounted() { 55 | this.generate(); 56 | }, 57 | 58 | methods: { 59 | /** 60 | * Generate QR code. 61 | */ 62 | generate() { 63 | const options = this.options || {}; 64 | const value = String(this.value); 65 | const done = () => { 66 | this.$emit(EVENT_READY, this.$el); 67 | }; 68 | 69 | switch (this.tag) { 70 | case 'canvas': 71 | toCanvas(this.$el, value, options, (error) => { 72 | if (error) { 73 | throw error; 74 | } 75 | 76 | done(); 77 | }); 78 | break; 79 | 80 | case 'img': 81 | toDataURL(value, options, (error, url) => { 82 | if (error) { 83 | throw error; 84 | } 85 | 86 | this.$el.src = url; 87 | this.$el.onload = done; 88 | }); 89 | break; 90 | 91 | case 'svg': 92 | toString(value, options, (error, string) => { 93 | if (error) { 94 | throw error; 95 | } 96 | 97 | const div = document.createElement('div'); 98 | 99 | div.innerHTML = string; 100 | 101 | const svg = div.querySelector('svg'); 102 | 103 | if (svg) { 104 | const { attributes, childNodes } = svg; 105 | 106 | Object.keys(attributes).forEach((key: string) => { 107 | const attribute = attributes[Number(key)]; 108 | 109 | this.$el.setAttribute(attribute.name, attribute.value); 110 | }); 111 | Object.keys(childNodes).forEach((key: string) => { 112 | const childNode = childNodes[Number(key)]; 113 | 114 | this.$el.appendChild(childNode.cloneNode(true)); 115 | }); 116 | done(); 117 | } 118 | }); 119 | break; 120 | 121 | default: 122 | } 123 | }, 124 | }, 125 | 126 | render() { 127 | return h(this.tag, this.$slots.default); 128 | }, 129 | }); 130 | -------------------------------------------------------------------------------- /src/shims.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.md' { 2 | const content: any; 3 | 4 | export default content; 5 | } 6 | -------------------------------------------------------------------------------- /stylelint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: 'stylelint-config-recommended-vue/scss', 3 | plugins: [ 4 | 'stylelint-order', 5 | ], 6 | rules: { 7 | 'no-descending-specificity': null, 8 | 'no-empty-source': null, 9 | 'order/properties-alphabetical-order': true, 10 | }, 11 | }; 12 | -------------------------------------------------------------------------------- /tests/events.spec.ts: -------------------------------------------------------------------------------- 1 | import { mount } from '@vue/test-utils'; 2 | import VueQrcode from '../src'; 3 | 4 | describe('events', () => { 5 | describe('custom', () => { 6 | it('should trigger the custom `ready` event', (done) => { 7 | mount({ 8 | components: { 9 | VueQrcode, 10 | }, 11 | methods: { 12 | onReady(element: HTMLCanvasElement) { 13 | expect(element).toBeInstanceOf(HTMLCanvasElement); 14 | done(); 15 | }, 16 | }, 17 | template: '', 18 | }); 19 | }); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /tests/props.spec.ts: -------------------------------------------------------------------------------- 1 | import { mount } from '@vue/test-utils'; 2 | import VueQrcode from '../src'; 3 | 4 | describe('props', () => { 5 | describe('options', () => { 6 | it('should apply the options', (done) => { 7 | const wrapper = mount(VueQrcode, { 8 | props: { 9 | value: 'foo', 10 | options: { 11 | width: 200, 12 | }, 13 | }, 14 | }); 15 | 16 | setTimeout(() => { 17 | expect(wrapper.vm.$el.width).toBe(200); 18 | done(); 19 | }, 100); 20 | }); 21 | 22 | it('should update the QR code automatically when the options changed', (done) => { 23 | const wrapper = mount({ 24 | components: { 25 | VueQrcode, 26 | }, 27 | data() { 28 | return { 29 | options: { 30 | width: 200, 31 | }, 32 | }; 33 | }, 34 | mounted() { 35 | setTimeout(() => { 36 | expect(wrapper.vm.$el.width).toBe(200); 37 | this.options.width = 400; 38 | setTimeout(() => { 39 | expect(wrapper.vm.$el.width).toBe(400); 40 | done(); 41 | }, 100); 42 | }, 100); 43 | }, 44 | template: '', 45 | }); 46 | }); 47 | }); 48 | 49 | describe('tag', () => { 50 | it('should be "canvas" by default', () => { 51 | const wrapper = mount(VueQrcode, { 52 | props: { 53 | value: 'foo', 54 | }, 55 | }); 56 | 57 | expect(wrapper.props('tag')).toBe('canvas'); 58 | expect(wrapper.vm.$el.tagName.toLowerCase()).toBe('canvas'); 59 | }); 60 | 61 | it('should be "img"', () => { 62 | const wrapper = mount(VueQrcode, { 63 | props: { 64 | value: 'foo', 65 | tag: 'img', 66 | }, 67 | }); 68 | 69 | expect(wrapper.props('tag')).toBe('img'); 70 | expect(wrapper.vm.$el.tagName.toLowerCase()).toBe('img'); 71 | }); 72 | 73 | it('should be "svg"', () => { 74 | const wrapper = mount(VueQrcode, { 75 | props: { 76 | value: 'foo', 77 | tag: 'svg', 78 | }, 79 | }); 80 | 81 | expect(wrapper.props('tag')).toBe('svg'); 82 | expect(wrapper.vm.$el.tagName.toLowerCase()).toBe('svg'); 83 | }); 84 | }); 85 | 86 | describe('value', () => { 87 | it('should be undefined by default', () => { 88 | const wrapper = mount(VueQrcode); 89 | 90 | expect(wrapper.props('value')).toBeUndefined(); 91 | }); 92 | 93 | it('should match the given value', () => { 94 | const wrapper = mount(VueQrcode, { 95 | props: { 96 | value: 'foo', 97 | }, 98 | }); 99 | 100 | expect(wrapper.props('value')).toBe('foo'); 101 | }); 102 | 103 | it('should update the QR code automatically when the value changed', (done) => { 104 | const wrapper = mount({ 105 | components: { 106 | VueQrcode, 107 | }, 108 | data() { 109 | return { 110 | value: 'foo', 111 | }; 112 | }, 113 | mounted() { 114 | setTimeout(() => { 115 | const oldDataURL = wrapper.vm.$el.toDataURL(); 116 | 117 | this.value = 'bar'; 118 | setTimeout(() => { 119 | const newDataURL = wrapper.vm.$el.toDataURL(); 120 | 121 | expect(newDataURL).not.toBe(oldDataURL); 122 | done(); 123 | }, 100); 124 | }, 100); 125 | }, 126 | template: '', 127 | }); 128 | }); 129 | }); 130 | }); 131 | -------------------------------------------------------------------------------- /tsconfig.eslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig", 3 | "include": [ 4 | "*.js", 5 | ".*.js", 6 | "docs/**/*", 7 | "src/**/*", 8 | "tests/**/*" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": true, 4 | "moduleResolution": "node", 5 | "resolveJsonModule": true, 6 | "strict": true, 7 | "target": "esnext", 8 | }, 9 | "include": [ 10 | "src/**/*", 11 | "docs/**/*", 12 | "tests/**/*" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const webpack = require('webpack'); 3 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 4 | const VueLoaderPlugin = require('vue-loader/dist/plugin').default; 5 | const MiniCssExtractPlugin = require('mini-css-extract-plugin'); 6 | const markdownItAnchor = require('markdown-it-anchor'); 7 | 8 | module.exports = (env) => ({ 9 | mode: env.production ? 'production' : 'development', 10 | entry: './docs', 11 | output: { 12 | path: path.resolve(__dirname, './docs/dist'), 13 | }, 14 | module: { 15 | rules: [ 16 | { 17 | test: /\.js$/, 18 | use: 'babel-loader', 19 | }, 20 | { 21 | test: /\.ts$/, 22 | loader: 'ts-loader', 23 | options: { 24 | appendTsSuffixTo: [/\.vue$/], 25 | }, 26 | }, 27 | { 28 | test: /\.vue$/, 29 | loader: 'vue-loader', 30 | }, 31 | { 32 | test: /\.scss$/, 33 | use: [ 34 | MiniCssExtractPlugin.loader, 35 | 'css-loader', 36 | 'sass-loader', 37 | ], 38 | }, 39 | { 40 | test: /\.md$/, 41 | use: [ 42 | 'vue-loader', 43 | { 44 | loader: 'markdown-to-vue-loader', 45 | options: { 46 | componentWrapper: '', 47 | tableClass: 'table', 48 | tableWrapper: '
', 49 | configureMarkdownIt(md) { 50 | md.use(markdownItAnchor, { 51 | permalink: markdownItAnchor.permalink.headerLink(), 52 | }); 53 | }, 54 | }, 55 | }, 56 | ], 57 | }, 58 | ], 59 | }, 60 | plugins: [ 61 | new HtmlWebpackPlugin({ 62 | filename: 'index.html', 63 | template: './docs/index.html', 64 | }), 65 | new MiniCssExtractPlugin(), 66 | new VueLoaderPlugin(), 67 | new webpack.DefinePlugin({ 68 | __VUE_OPTIONS_API__: true, 69 | __VUE_PROD_DEVTOOLS__: false, 70 | }), 71 | ], 72 | externals: env.production ? { 73 | vue: 'Vue', 74 | qrcode: 'QRCode', 75 | } : {}, 76 | resolve: { 77 | alias: { 78 | vue$: 'vue/dist/vue.esm-bundler', 79 | }, 80 | extensions: ['.js', '.json', '.ts', '.d.ts', '.vue'], 81 | }, 82 | }); 83 | --------------------------------------------------------------------------------