├── .babelrc
├── .gitignore
├── .postcssrc.js
├── LICENSE
├── README.md
├── _config.yml
├── demo
├── App.vue
├── assets
│ ├── dinosaur.svg
│ ├── robot.svg
│ └── ufo.svg
├── build
│ ├── build.js
│ ├── check-versions.js
│ ├── logo.png
│ ├── utils.js
│ ├── vue-loader.conf.js
│ ├── webpack.base.conf.js
│ ├── webpack.dev.conf.js
│ ├── webpack.prod.conf.js
│ └── webpack.test.conf.js
├── components
│ ├── ChangeSrcButton.vue
│ └── SvgButton.vue
├── config
│ ├── dev.env.js
│ ├── index.js
│ ├── prod.env.js
│ └── test.env.js
├── main.js
└── template.html
├── dist
└── plugin.js
├── package-lock.json
├── package.json
├── rollup.config.js
└── src
└── plugin.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["env", {
4 | "modules": false
5 | }],
6 | "stage-2"
7 | ],
8 | "plugins": ["transform-runtime"],
9 | "env": {
10 | "test": {
11 | "presets": ["env", "stage-2"],
12 | "plugins": ["transform-es2015-modules-commonjs", "dynamic-import-node"]
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules/
3 | npm-debug.log*
4 | yarn-debug.log*
5 | yarn-error.log*
6 | /test/unit/coverage/
7 | /test/e2e/reports/
8 | selenium-debug.log
9 |
10 | # Editor directories and files
11 | .idea
12 | .vscode
13 | *.suo
14 | *.ntvs*
15 | *.njsproj
16 | *.sln
17 |
--------------------------------------------------------------------------------
/.postcssrc.js:
--------------------------------------------------------------------------------
1 | // https://github.com/michael-ciniawsky/postcss-load-config
2 |
3 | module.exports = {
4 | "plugins": {
5 | // to edit target browsers: use "browserslist" field in package.json
6 | "postcss-import": {},
7 | "autoprefixer": {}
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Seiya Kobayashi
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 | # vue-simple-svg (V2)
2 | A simple Vue.js plugin that allows you to use a component that dynamically loads a .svg file as an inline SVG so you can easily control its style programmatically. No jQuery required.
3 |
4 | I recommend using [vue-svg-loader](https://www.npmjs.com/package/vue-svg-loader) for many cases when you just need to load a SVG file as a component. This plugin is built to cover some other cases the library doesn't fit, which are:
5 | - loading a SVG file dynamically. You don't have to hardcode the filename in the source code. Instead you can specify it at rendering time or change it even after the component is rendered.
6 | - changing fill color or stroke color of the SVG programmatically with ease and no global css usage.
7 |
8 | ### Installation:
9 | ```sh
10 | $ npm install vue-simple-svg
11 | ```
12 |
13 | ### Usage:
14 | 1. initialize in your main file,
15 | ```javascript
16 | // as a plugin
17 | import VueSimpleSVG from 'vue-simple-svg'
18 | Vue.use(VueSimpleSVG)
19 |
20 | // or as a component
21 | import {SimpleSVG} from 'vue-simple-svg'
22 | Vue.component('simple-svg', SimpleSVG)
23 | ```
24 |
25 | 2. specify which elements in the SVG will be manipulated their fill and stroke colors by setting dedicated class names to them
26 | ```html
27 |
28 |
29 |
30 |
31 |
32 |
33 | ```
34 |
35 | 3. and use it in your component,
36 | ```html
37 |
49 | ```
50 |
51 | ### Available props and events:
52 | | props | type | description | default |
53 | | ------ | ------ | ------ | ------ |
54 | | src | string | path to your SVG file | *required |
55 | | fillClassName | string | class name set to the elements in your SVG file whose fill color you want to change | '' |
56 | | fill | string | CSS-valid fill color value | '' |
57 | | strokeClassName | string | class name set to the elements in your SVG file whose stroke color you want to change | '' |
58 | | stroke | string | CSS-valid stroke color value | '' |
59 | | width | string | root SVG element's style width | 'auto' |
60 | | height | string | root SVG element's style height | 'auto' |
61 | | customId | string | root SVG element's id | '' |
62 | | customClassName | string | root SVG element's class | '' |
63 |
64 | | events | description |
65 | | ------ | ------ |
66 | | @load | called when the inline SVG is generated |
67 |
68 |
69 | ### Notes:
70 | - To generate the inline SVG properly, you need to manually clean up and edit your SVG files beforehand. Tips: remove all hardcoded inline styles and unnecessary attributes, especially the ones specifying colors.
71 |
72 | ### Demo:
73 | 
74 |
75 | To run demo in your local environment,
76 | ```sh
77 | $ npm run dev-demo
78 | ```
79 | You can see the example of how to use simple-svg component at demo/components/SvgButton.vue
80 |
81 | ### Reference:
82 | - Loading a SVG with XMLHttpRequest and DOMParser https://github.com/jonnyhaynes/inline-svg
83 | - Parsing inline svg tags https://github.com/MMF-FE/vue-svgicon
84 |
--------------------------------------------------------------------------------
/_config.yml:
--------------------------------------------------------------------------------
1 | theme: jekyll-theme-cayman
--------------------------------------------------------------------------------
/demo/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
15 |
45 |
46 |
69 |
--------------------------------------------------------------------------------
/demo/assets/dinosaur.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/demo/assets/robot.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/demo/assets/ufo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/demo/build/build.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | require('./check-versions')()
3 |
4 | process.env.NODE_ENV = 'production'
5 |
6 | const ora = require('ora')
7 | const rm = require('rimraf')
8 | const path = require('path')
9 | const chalk = require('chalk')
10 | const webpack = require('webpack')
11 | const config = require('../config')
12 | const webpackConfig = require('./webpack.prod.conf')
13 |
14 | const spinner = ora('building for production...')
15 | spinner.start()
16 |
17 | rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
18 | if (err) throw err
19 | webpack(webpackConfig, (err, stats) => {
20 | spinner.stop()
21 | if (err) throw err
22 | process.stdout.write(stats.toString({
23 | colors: true,
24 | modules: false,
25 | children: false,
26 | chunks: false,
27 | chunkModules: false
28 | }) + '\n\n')
29 |
30 | if (stats.hasErrors()) {
31 | console.log(chalk.red(' Build failed with errors.\n'))
32 | process.exit(1)
33 | }
34 |
35 | console.log(chalk.cyan(' Build complete.\n'))
36 | console.log(chalk.yellow(
37 | ' Tip: built files are meant to be served over an HTTP server.\n' +
38 | ' Opening index.html over file:// won\'t work.\n'
39 | ))
40 | })
41 | })
42 |
--------------------------------------------------------------------------------
/demo/build/check-versions.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const chalk = require('chalk')
3 | const semver = require('semver')
4 | const packageConfig = require('../package.json')
5 | const shell = require('shelljs')
6 |
7 | function exec (cmd) {
8 | return require('child_process').execSync(cmd).toString().trim()
9 | }
10 |
11 | const versionRequirements = [
12 | {
13 | name: 'node',
14 | currentVersion: semver.clean(process.version),
15 | versionRequirement: packageConfig.engines.node
16 | }
17 | ]
18 |
19 | if (shell.which('npm')) {
20 | versionRequirements.push({
21 | name: 'npm',
22 | currentVersion: exec('npm --version'),
23 | versionRequirement: packageConfig.engines.npm
24 | })
25 | }
26 |
27 | module.exports = function () {
28 | const warnings = []
29 |
30 | for (let i = 0; i < versionRequirements.length; i++) {
31 | const mod = versionRequirements[i]
32 |
33 | if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
34 | warnings.push(mod.name + ': ' +
35 | chalk.red(mod.currentVersion) + ' should be ' +
36 | chalk.green(mod.versionRequirement)
37 | )
38 | }
39 | }
40 |
41 | if (warnings.length) {
42 | console.log('')
43 | console.log(chalk.yellow('To use this template, you must update following to modules:'))
44 | console.log()
45 |
46 | for (let i = 0; i < warnings.length; i++) {
47 | const warning = warnings[i]
48 | console.log(' ' + warning)
49 | }
50 |
51 | console.log()
52 | process.exit(1)
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/demo/build/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seiyable/vue-simple-svg/451596980332e19d684bebb0c584f6005d799bef/demo/build/logo.png
--------------------------------------------------------------------------------
/demo/build/utils.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const path = require('path')
3 | const config = require('../config')
4 | const ExtractTextPlugin = require('extract-text-webpack-plugin')
5 | const packageConfig = require('../../package.json') // '../package.json' /* --changed-- */
6 |
7 | exports.assetsPath = function (_path) {
8 | const assetsSubDirectory = process.env.NODE_ENV === 'production'
9 | ? config.build.assetsSubDirectory
10 | : config.dev.assetsSubDirectory
11 |
12 | return path.posix.join(assetsSubDirectory, _path)
13 | }
14 |
15 | exports.cssLoaders = function (options) {
16 | options = options || {}
17 |
18 | const cssLoader = {
19 | loader: 'css-loader',
20 | options: {
21 | sourceMap: options.sourceMap
22 | }
23 | }
24 |
25 | const postcssLoader = {
26 | loader: 'postcss-loader',
27 | options: {
28 | sourceMap: options.sourceMap
29 | }
30 | }
31 |
32 | // generate loader string to be used with extract text plugin
33 | function generateLoaders (loader, loaderOptions) {
34 | const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader]
35 |
36 | if (loader) {
37 | loaders.push({
38 | loader: loader + '-loader',
39 | options: Object.assign({}, loaderOptions, {
40 | sourceMap: options.sourceMap
41 | })
42 | })
43 | }
44 |
45 | // Extract CSS when that option is specified
46 | // (which is the case during production build)
47 | if (options.extract) {
48 | return ExtractTextPlugin.extract({
49 | use: loaders,
50 | fallback: 'vue-style-loader'
51 | })
52 | } else {
53 | return ['vue-style-loader'].concat(loaders)
54 | }
55 | }
56 |
57 | // https://vue-loader.vuejs.org/en/configurations/extract-css.html
58 | return {
59 | css: generateLoaders(),
60 | postcss: generateLoaders(),
61 | less: generateLoaders('less'),
62 | sass: generateLoaders('sass', { indentedSyntax: true }),
63 | scss: generateLoaders('sass'),
64 | stylus: generateLoaders('stylus'),
65 | styl: generateLoaders('stylus')
66 | }
67 | }
68 |
69 | // Generate loaders for standalone style files (outside of .vue)
70 | exports.styleLoaders = function (options) {
71 | const output = []
72 | const loaders = exports.cssLoaders(options)
73 |
74 | for (const extension in loaders) {
75 | const loader = loaders[extension]
76 | output.push({
77 | test: new RegExp('\\.' + extension + '$'),
78 | use: loader
79 | })
80 | }
81 |
82 | return output
83 | }
84 |
85 | exports.createNotifierCallback = () => {
86 | const notifier = require('node-notifier')
87 |
88 | return (severity, errors) => {
89 | if (severity !== 'error') return
90 |
91 | const error = errors[0]
92 | const filename = error.file && error.file.split('!').pop()
93 |
94 | notifier.notify({
95 | title: packageConfig.name,
96 | message: severity + ': ' + error.name,
97 | subtitle: filename || '',
98 | icon: path.join(__dirname, 'logo.png')
99 | })
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/demo/build/vue-loader.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const utils = require('./utils')
3 | const config = require('../config')
4 | const isProduction = process.env.NODE_ENV === 'production'
5 | const sourceMapEnabled = isProduction
6 | ? config.build.productionSourceMap
7 | : config.dev.cssSourceMap
8 |
9 | module.exports = {
10 | loaders: utils.cssLoaders({
11 | sourceMap: sourceMapEnabled,
12 | extract: isProduction
13 | }),
14 | cssSourceMap: sourceMapEnabled,
15 | cacheBusting: config.dev.cacheBusting,
16 | transformToRequire: {
17 | video: ['src', 'poster'],
18 | source: 'src',
19 | img: 'src',
20 | image: 'xlink:href'
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/demo/build/webpack.base.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const path = require('path')
3 | const utils = require('./utils')
4 | const config = require('../config')
5 | const vueLoaderConfig = require('./vue-loader.conf')
6 |
7 | function resolve (dir) {
8 | return path.join(__dirname, '..', dir)
9 | }
10 |
11 | const createLintingRule = () => ({
12 | test: /\.(js|vue)$/,
13 | loader: 'eslint-loader',
14 | enforce: 'pre',
15 | /* --changed-- */
16 | include: [resolve('demo'), resolve('test')], // 'src'
17 | options: {
18 | formatter: require('eslint-friendly-formatter'),
19 | emitWarning: !config.dev.showEslintErrorsInOverlay
20 | }
21 | })
22 |
23 | module.exports = {
24 | context: path.resolve(__dirname, '../'),
25 | entry: {
26 | /* --changed-- */
27 | app: './main.js' // ./src/main.js
28 | },
29 | output: {
30 | path: config.build.assetsRoot,
31 | filename: '[name].js',
32 | publicPath: process.env.NODE_ENV === 'production'
33 | ? config.build.assetsPublicPath
34 | : config.dev.assetsPublicPath
35 | },
36 | resolve: {
37 | extensions: ['.js', '.vue', '.json'],
38 | alias: {
39 | 'vue$': 'vue/dist/vue.esm.js',
40 | '@': resolve('demo'),
41 | }
42 | },
43 | module: {
44 | rules: [
45 | {
46 | test: /\.vue$/,
47 | loader: 'vue-loader',
48 | options: vueLoaderConfig
49 | },
50 | {
51 | test: /\.js$/,
52 | loader: 'babel-loader',
53 | /* --changed-- */
54 | include: [resolve('demo'), resolve('test')] // 'src'
55 | },
56 | {
57 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
58 | loader: 'url-loader',
59 | options: {
60 | limit: 10000,
61 | name: utils.assetsPath('img/[name].[hash:7].[ext]')
62 | }
63 | },
64 | {
65 | test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
66 | loader: 'url-loader',
67 | options: {
68 | limit: 10000,
69 | name: utils.assetsPath('media/[name].[hash:7].[ext]')
70 | }
71 | },
72 | {
73 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
74 | loader: 'url-loader',
75 | options: {
76 | limit: 10000,
77 | name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
78 | }
79 | }
80 | ]
81 | },
82 | node: {
83 | // prevent webpack from injecting useless setImmediate polyfill because Vue
84 | // source contains it (although only uses it if it's native).
85 | setImmediate: false,
86 | // prevent webpack from injecting mocks to Node native modules
87 | // that does not make sense for the client
88 | dgram: 'empty',
89 | fs: 'empty',
90 | net: 'empty',
91 | tls: 'empty',
92 | child_process: 'empty'
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/demo/build/webpack.dev.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const utils = require('./utils')
3 | const webpack = require('webpack')
4 | const config = require('../config')
5 | const merge = require('webpack-merge')
6 | const baseWebpackConfig = require('./webpack.base.conf')
7 | const HtmlWebpackPlugin = require('html-webpack-plugin')
8 | const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
9 | const portfinder = require('portfinder')
10 |
11 | const HOST = process.env.HOST
12 | const PORT = process.env.PORT && Number(process.env.PORT)
13 |
14 | const devWebpackConfig = merge(baseWebpackConfig, {
15 | module: {
16 | rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true })
17 | },
18 | // cheap-module-eval-source-map is faster for development
19 | devtool: config.dev.devtool,
20 |
21 | // these devServer options should be customized in /config/index.js
22 | devServer: {
23 | clientLogLevel: 'warning',
24 | historyApiFallback: true,
25 | hot: true,
26 | compress: true,
27 | host: HOST || config.dev.host,
28 | port: PORT || config.dev.port,
29 | open: config.dev.autoOpenBrowser,
30 | overlay: config.dev.errorOverlay
31 | ? { warnings: false, errors: true }
32 | : false,
33 | publicPath: config.dev.assetsPublicPath,
34 | proxy: config.dev.proxyTable,
35 | quiet: true, // necessary for FriendlyErrorsPlugin
36 | watchOptions: {
37 | poll: config.dev.poll,
38 | }
39 | },
40 | plugins: [
41 | new webpack.DefinePlugin({
42 | 'process.env': require('../config/dev.env')
43 | }),
44 | new webpack.HotModuleReplacementPlugin(),
45 | new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update.
46 | new webpack.NoEmitOnErrorsPlugin(),
47 | // https://github.com/ampedandwired/html-webpack-plugin
48 | new HtmlWebpackPlugin({
49 | /* --changed-- */
50 | filename: 'index.html', // 'index.html'
51 | template: 'template.html', // 'index.html'
52 | inject: true
53 | }),
54 | ]
55 | })
56 |
57 | module.exports = new Promise((resolve, reject) => {
58 | portfinder.basePort = process.env.PORT || config.dev.port
59 | portfinder.getPort((err, port) => {
60 | if (err) {
61 | reject(err)
62 | } else {
63 | // publish the new Port, necessary for e2e tests
64 | process.env.PORT = port
65 | // add port to devServer config
66 | devWebpackConfig.devServer.port = port
67 |
68 | // Add FriendlyErrorsPlugin
69 | devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({
70 | compilationSuccessInfo: {
71 | /* --changed-- */
72 | messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}/demo`],
73 | },
74 | onErrors: config.dev.notifyOnErrors
75 | ? utils.createNotifierCallback()
76 | : undefined
77 | }))
78 |
79 | resolve(devWebpackConfig)
80 | }
81 | })
82 | })
83 |
--------------------------------------------------------------------------------
/demo/build/webpack.prod.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const path = require('path')
3 | const utils = require('./utils')
4 | const webpack = require('webpack')
5 | const config = require('../config')
6 | const merge = require('webpack-merge')
7 | const baseWebpackConfig = require('./webpack.base.conf')
8 | const CopyWebpackPlugin = require('copy-webpack-plugin')
9 | const HtmlWebpackPlugin = require('html-webpack-plugin')
10 | const ExtractTextPlugin = require('extract-text-webpack-plugin')
11 | const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
12 | const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
13 |
14 | const env = process.env.NODE_ENV === 'testing'
15 | ? require('../config/test.env')
16 | : require('../config/prod.env')
17 |
18 | const webpackConfig = merge(baseWebpackConfig, {
19 | module: {
20 | rules: utils.styleLoaders({
21 | sourceMap: config.build.productionSourceMap,
22 | extract: true,
23 | usePostCSS: true
24 | })
25 | },
26 | devtool: config.build.productionSourceMap ? config.build.devtool : false,
27 | output: {
28 | path: config.build.assetsRoot,
29 | filename: utils.assetsPath('js/[name].[chunkhash].js'),
30 | chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
31 | },
32 | plugins: [
33 | // http://vuejs.github.io/vue-loader/en/workflow/production.html
34 | new webpack.DefinePlugin({
35 | 'process.env': env
36 | }),
37 | new UglifyJsPlugin({
38 | uglifyOptions: {
39 | compress: {
40 | warnings: false
41 | }
42 | },
43 | sourceMap: config.build.productionSourceMap,
44 | parallel: true
45 | }),
46 | // extract css into its own file
47 | new ExtractTextPlugin({
48 | filename: utils.assetsPath('css/[name].[contenthash].css'),
49 | // set the following option to `true` if you want to extract CSS from
50 | // codesplit chunks into this main css file as well.
51 | // This will result in *all* of your app's CSS being loaded upfront.
52 | allChunks: false,
53 | }),
54 | // Compress extracted CSS. We are using this plugin so that possible
55 | // duplicated CSS from different components can be deduped.
56 | new OptimizeCSSPlugin({
57 | cssProcessorOptions: config.build.productionSourceMap
58 | ? { safe: true, map: { inline: false } }
59 | : { safe: true }
60 | }),
61 | // generate dist index.html with correct asset hash for caching.
62 | // you can customize output by editing /index.html
63 | // see https://github.com/ampedandwired/html-webpack-plugin
64 | new HtmlWebpackPlugin({
65 | /* --changed-- */
66 | filename: process.env.NODE_ENV === 'testing'
67 | ? 'demo/index.html' // 'index.html'
68 | : config.build.index,
69 | template: 'demo/template.html', // 'index.html'
70 | inject: true,
71 | minify: {
72 | removeComments: true,
73 | collapseWhitespace: true,
74 | removeAttributeQuotes: true
75 | // more options:
76 | // https://github.com/kangax/html-minifier#options-quick-reference
77 | },
78 | // necessary to consistently work with multiple chunks via CommonsChunkPlugin
79 | chunksSortMode: 'dependency'
80 | }),
81 | // keep module.id stable when vender modules does not change
82 | new webpack.HashedModuleIdsPlugin(),
83 | // enable scope hoisting
84 | new webpack.optimize.ModuleConcatenationPlugin(),
85 | // split vendor js into its own file
86 | new webpack.optimize.CommonsChunkPlugin({
87 | name: 'vendor',
88 | minChunks (module) {
89 | // any required modules inside node_modules are extracted to vendor
90 | return (
91 | module.resource &&
92 | /\.js$/.test(module.resource) &&
93 | module.resource.indexOf(
94 | path.join(__dirname, '../node_modules')
95 | ) === 0
96 | )
97 | }
98 | }),
99 | // extract webpack runtime and module manifest to its own file in order to
100 | // prevent vendor hash from being updated whenever app bundle is updated
101 | new webpack.optimize.CommonsChunkPlugin({
102 | name: 'manifest',
103 | minChunks: Infinity
104 | }),
105 | // This instance extracts shared chunks from code splitted chunks and bundles them
106 | // in a separate chunk, similar to the vendor chunk
107 | // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk
108 | new webpack.optimize.CommonsChunkPlugin({
109 | name: 'app',
110 | async: 'vendor-async',
111 | children: true,
112 | minChunks: 3
113 | }),
114 |
115 | // copy custom static assets
116 | new CopyWebpackPlugin([
117 | {
118 | from: path.resolve(__dirname, '../static'),
119 | to: config.build.assetsSubDirectory,
120 | ignore: ['.*']
121 | }
122 | ])
123 | ]
124 | })
125 |
126 | if (config.build.productionGzip) {
127 | const CompressionWebpackPlugin = require('compression-webpack-plugin')
128 |
129 | webpackConfig.plugins.push(
130 | new CompressionWebpackPlugin({
131 | asset: '[path].gz[query]',
132 | algorithm: 'gzip',
133 | test: new RegExp(
134 | '\\.(' +
135 | config.build.productionGzipExtensions.join('|') +
136 | ')$'
137 | ),
138 | threshold: 10240,
139 | minRatio: 0.8
140 | })
141 | )
142 | }
143 |
144 | if (config.build.bundleAnalyzerReport) {
145 | const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
146 | webpackConfig.plugins.push(new BundleAnalyzerPlugin())
147 | }
148 |
149 | module.exports = webpackConfig
150 |
--------------------------------------------------------------------------------
/demo/build/webpack.test.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | // This is the webpack config used for unit tests.
3 |
4 | const utils = require('./utils')
5 | const webpack = require('webpack')
6 | const merge = require('webpack-merge')
7 | const baseWebpackConfig = require('./webpack.base.conf')
8 |
9 | const webpackConfig = merge(baseWebpackConfig, {
10 | // use inline sourcemap for karma-sourcemap-loader
11 | module: {
12 | rules: utils.styleLoaders()
13 | },
14 | devtool: '#inline-source-map',
15 | resolveLoader: {
16 | alias: {
17 | // necessary to to make lang="scss" work in test when using vue-loader's ?inject option
18 | // see discussion at https://github.com/vuejs/vue-loader/issues/724
19 | 'scss-loader': 'sass-loader'
20 | }
21 | },
22 | plugins: [
23 | new webpack.DefinePlugin({
24 | 'process.env': require('../config/test.env')
25 | })
26 | ]
27 | })
28 |
29 | // no need for app entry during tests
30 | delete webpackConfig.entry
31 |
32 | module.exports = webpackConfig
33 |
--------------------------------------------------------------------------------
/demo/components/ChangeSrcButton.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 | Click to change SVG src
7 |
8 |
9 |
10 |
25 |
--------------------------------------------------------------------------------
/demo/components/SvgButton.vue:
--------------------------------------------------------------------------------
1 |
2 |
25 |
26 |
27 |
64 |
65 |
90 |
--------------------------------------------------------------------------------
/demo/config/dev.env.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const merge = require('webpack-merge')
3 | const prodEnv = require('./prod.env')
4 |
5 | module.exports = merge(prodEnv, {
6 | NODE_ENV: '"development"'
7 | })
8 |
--------------------------------------------------------------------------------
/demo/config/index.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | // Template version: 1.2.5
3 | // see http://vuejs-templates.github.io/webpack for documentation.
4 |
5 | const path = require('path')
6 |
7 | module.exports = {
8 | dev: {
9 |
10 | // Paths
11 | /* --changed-- */
12 | assetsSubDirectory: 'static',
13 | assetsPublicPath: '/demo', // /
14 | proxyTable: {},
15 |
16 | // Various Dev Server settings
17 | host: 'localhost', // can be overwritten by process.env.HOST
18 | port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
19 | autoOpenBrowser: false,
20 | errorOverlay: true,
21 | notifyOnErrors: true,
22 | poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-
23 |
24 | // Use Eslint Loader?
25 | // If true, your code will be linted during bundling and
26 | // linting errors and warnings will be shown in the console.
27 | useEslint: true,
28 | // If true, eslint errors and warnings will also be shown in the error overlay
29 | // in the browser.
30 | showEslintErrorsInOverlay: false,
31 |
32 | /**
33 | * Source Maps
34 | */
35 |
36 | // https://webpack.js.org/configuration/devtool/#development
37 | devtool: 'eval-source-map',
38 |
39 | // If you have problems debugging vue-files in devtools,
40 | // set this to false - it *may* help
41 | // https://vue-loader.vuejs.org/en/options.html#cachebusting
42 | cacheBusting: true,
43 |
44 | // CSS Sourcemaps off by default because relative paths are "buggy"
45 | // with this option, according to the CSS-Loader README
46 | // (https://github.com/webpack/css-loader#sourcemaps)
47 | // In our experience, they generally work as expected,
48 | // just be aware of this issue when enabling this option.
49 | cssSourceMap: false,
50 | },
51 |
52 | build: {
53 | // Template for index.html
54 | /* --changed-- */
55 | index: path.resolve(__dirname, '../demo/template.html'), // ../dist/index.html
56 |
57 | // Paths
58 | /* --changed-- */
59 | assetsRoot: path.resolve(__dirname, '../demo/dist'), // /dist
60 | assetsSubDirectory: 'static',
61 | assetsPublicPath: '/demo', // /
62 |
63 | /**
64 | * Source Maps
65 | */
66 |
67 | productionSourceMap: true,
68 | // https://webpack.js.org/configuration/devtool/#production
69 | devtool: '#source-map',
70 |
71 | // Gzip off by default as many popular static hosts such as
72 | // Surge or Netlify already gzip all static assets for you.
73 | // Before setting to `true`, make sure to:
74 | // npm install --save-dev compression-webpack-plugin
75 | productionGzip: false,
76 | productionGzipExtensions: ['js', 'css'],
77 |
78 | // Run the build command with an extra argument to
79 | // View the bundle analyzer report after build finishes:
80 | // `npm run build --report`
81 | // Set to `true` or `false` to always turn it on or off
82 | bundleAnalyzerReport: process.env.npm_config_report
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/demo/config/prod.env.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | module.exports = {
3 | NODE_ENV: '"production"'
4 | }
5 |
--------------------------------------------------------------------------------
/demo/config/test.env.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const merge = require('webpack-merge')
3 | const devEnv = require('./dev.env')
4 |
5 | module.exports = merge(devEnv, {
6 | NODE_ENV: '"testing"'
7 | })
8 |
--------------------------------------------------------------------------------
/demo/main.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import App from './App'
3 |
4 | // import VueSimpleSVG from '../dist/plugin.js'
5 | import VueSimpleSVG from '../src/plugin.js'
6 | Vue.use(VueSimpleSVG)
7 |
8 | new Vue({
9 | el: '#app',
10 | template: ' ',
11 | components: { App }
12 | })
13 |
--------------------------------------------------------------------------------
/demo/template.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | vue-template
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/dist/plugin.js:
--------------------------------------------------------------------------------
1 | "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var CSSOM=require("cssom"),SimpleSVG={render:function(e){return e("svg")},name:"simple-svg",props:{src:{type:String,required:!0},fillClassName:{type:String,default:""},fill:{type:String,default:""},strokeClassName:{type:String,default:""},stroke:{type:String,default:""},width:{type:String,default:"auto"},height:{type:String,default:"auto"},customId:{type:String,default:""},customClassName:{type:String,default:""}},mounted:function(){this.generateInlineSVG()},watch:{src:function(e){this.generateInlineSVG()},fill:function(e){this.updateFillColor(e)},stroke:function(e){this.updateStrokeColor(e)},width:function(e){this.$el.style.width=e},height:function(e){this.$el.style.width=e}},methods:{generateInlineSVG:function(){var e=this;this.resetInlineSVG();var t=new XMLHttpRequest;t.open("GET",this.src,!0),t.onload=function(){if(t.status>=200&&t.status<400){var r=(new DOMParser).parseFromString(t.responseText,"text/xml").getElementsByTagName("svg")[0];if(!r)return void console.error("No SVG element found in the given file: "+e.filepath);var i=r.getAttributeNames(),l=!0,s=!1,n=void 0;try{for(var o,a=i[Symbol.iterator]();!(l=(o=a.next()).done);l=!0){var u=o.value,f=r.getAttribute(u);e.$el.setAttribute(u,f)}}catch(e){s=!0,n=e}finally{try{!l&&a.return&&a.return()}finally{if(s)throw n}}e.customId&&(e.$el.id=e.customId),e.customClassName&&e.$el.setAttribute("class",e.customClassName),e.$el.style.width=e.width,e.$el.style.height=e.height;for(var h=r.children.length-1;h>=0;h--){var d=r.children.item(h);e.$el.appendChild(d)}e.updateFillColor(e.fill),e.updateStrokeColor(e.stroke),e.$emit("load")}else console.error("There was an error retrieving the source of the SVG.")},t.onerror=function(){console.error("There was on XML Http Request")},t.send()},resetInlineSVG:function(){for(;this.$el.firstChild;)this.$el.removeChild(this.$el.firstChild);var e=this.$el.getAttributeNames(),t=!0,r=!1,i=void 0;try{for(var l,s=e[Symbol.iterator]();!(t=(l=s.next()).done);t=!0){var n=l.value;this.$el.removeAttribute(n)}}catch(e){r=!0,i=e}finally{try{!t&&s.return&&s.return()}finally{if(r)throw i}}},updateFillColor:function(e){if(this.fillClassName){var t=this.$el.getElementsByClassName(this.fillClassName),r=!0,i=!1,l=void 0;try{for(var s,n=t[Symbol.iterator]();!(r=(s=n.next()).done);r=!0){s.value.style.fill=e}}catch(e){i=!0,l=e}finally{try{!r&&n.return&&n.return()}finally{if(i)throw l}}}},updateStrokeColor:function(e){if(this.strokeClassName){var t=this.$el.getElementsByClassName(this.strokeClassName),r=!0,i=!1,l=void 0;try{for(var s,n=t[Symbol.iterator]();!(r=(s=n.next()).done);r=!0){s.value.style.stroke=e}}catch(e){i=!0,l=e}finally{try{!r&&n.return&&n.return()}finally{if(i)throw l}}}}}},plugin={install:function(e,t){e.component("simple-svg",SimpleSVG)}};exports.default=plugin,exports.SimpleSVG=SimpleSVG;
2 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-simple-svg",
3 | "version": "2.0.2",
4 | "description": "A simple Vue.js plugin that allows you to use a component that loads a SVG image as an inline SVG so you can easily control its fill color from the parent component.",
5 | "main": "dist/plugin.js",
6 | "scripts": {
7 | "rollup": "rollup --config rollup.config.js",
8 | "dev-demo": "webpack-dev-server --inline --progress --config demo/build/webpack.dev.conf.js",
9 | "start": "npm run dev-demo",
10 | "build-demo": "node demo/build/build.js"
11 | },
12 | "repository": {
13 | "type": "git",
14 | "url": "https://github.com/seiyable/vue-simple-svg.git"
15 | },
16 | "keywords": [
17 | "vue svg",
18 | "vuejs svg",
19 | "vue.js svg",
20 | "vue inline svg"
21 | ],
22 | "author": "Seiya Kobayashi",
23 | "license": "MIT",
24 | "devDependencies": {
25 | "autoprefixer": "^7.2.6",
26 | "babel-cli": "^6.26.0",
27 | "babel-core": "^6.26.3",
28 | "babel-loader": "^7.1.4",
29 | "babel-plugin-dynamic-import-node": "^1.2.0",
30 | "babel-plugin-transform-es2015-modules-commonjs": "^6.26.2",
31 | "babel-plugin-transform-runtime": "^6.22.0",
32 | "babel-preset-env": "^1.7.0",
33 | "babel-preset-es2015-rollup": "^3.0.0",
34 | "babel-preset-stage-2": "^6.22.0",
35 | "babel-register": "^6.22.0",
36 | "chalk": "^2.4.1",
37 | "chromedriver": "^2.38.2",
38 | "copy-webpack-plugin": "^4.5.1",
39 | "cross-spawn": "^5.0.1",
40 | "css-loader": "^3.2.0",
41 | "eventsource-polyfill": "^0.9.6",
42 | "extract-text-webpack-plugin": "^3.0.0",
43 | "file-loader": "^1.1.11",
44 | "friendly-errors-webpack-plugin": "^1.7.0",
45 | "html-webpack-plugin": "^2.30.1",
46 | "node-notifier": "^5.2.1",
47 | "optimize-css-assets-webpack-plugin": "^5.0.3",
48 | "ora": "^1.4.0",
49 | "portfinder": "^1.0.13",
50 | "postcss-import": "^11.1.0",
51 | "postcss-loader": "^2.1.4",
52 | "reset.css": "^2.0.2",
53 | "rimraf": "^2.6.0",
54 | "rollup": "^0.52.3",
55 | "rollup-plugin-babel": "^3.0.4",
56 | "rollup-plugin-uglify": "^2.0.1",
57 | "selenium-server": "^3.11.0",
58 | "semver": "^5.5.0",
59 | "shelljs": "^0.7.6",
60 | "uglifyjs-webpack-plugin": "^1.2.5",
61 | "url-loader": "^1.1.1",
62 | "vue": "^2.5.16",
63 | "vue-loader": "^13.7.1",
64 | "vue-style-loader": "^3.1.2",
65 | "vue-template-compiler": "^2.5.16",
66 | "webpack": "^3.11.0",
67 | "webpack-bundle-analyzer": "^3.5.2",
68 | "webpack-dev-server": "^2.11.5",
69 | "webpack-merge": "^4.1.2"
70 | },
71 | "dependencies": {
72 | "cssom": "^0.3.2"
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | import babel from 'rollup-plugin-babel'
2 | import uglify from 'rollup-plugin-uglify'
3 |
4 | export default {
5 | input: 'src/plugin.js',
6 | output: {
7 | file: 'dist/plugin.js',
8 | format: 'cjs'
9 | },
10 | plugins: [
11 | babel({
12 | babelrc: false,
13 | presets: ["es2015-rollup"]
14 | }),
15 | uglify()
16 | ]
17 | };
18 |
--------------------------------------------------------------------------------
/src/plugin.js:
--------------------------------------------------------------------------------
1 | const CSSOM = require('cssom')
2 |
3 | const SimpleSVG = {
4 | render (createElement) {
5 | return createElement('svg')
6 | },
7 | name: 'simple-svg',
8 | props: {
9 | src: {
10 | type: String,
11 | required: true
12 | },
13 | fillClassName: {
14 | type: String,
15 | default: ''
16 | },
17 | fill: {
18 | type: String,
19 | default: ''
20 | },
21 | strokeClassName: {
22 | type: String,
23 | default: ''
24 | },
25 | stroke: {
26 | type: String,
27 | default: ''
28 | },
29 | width: {
30 | type: String,
31 | default: 'auto'
32 | },
33 | height: {
34 | type: String,
35 | default: 'auto'
36 | },
37 | customId: {
38 | type: String,
39 | default: ''
40 | },
41 | customClassName: {
42 | type: String,
43 | default: ''
44 | }
45 | },
46 | mounted () {
47 | this.generateInlineSVG()
48 | },
49 | watch: {
50 | src (val) {
51 | // Re-generate inline SVG if src is updated
52 | this.generateInlineSVG()
53 | },
54 | fill (val) {
55 | this.updateFillColor(val)
56 | },
57 | stroke (val) {
58 | this.updateStrokeColor(val)
59 | },
60 | width (val) {
61 | this.$el.style.width = val
62 | },
63 | height (val) {
64 | this.$el.style.width = val
65 | }
66 | },
67 | methods: {
68 | /* Load an SVG file with XHR and generate an inline SVG code */
69 | generateInlineSVG () {
70 | const context = this
71 |
72 | // Reset first. Remove all the code of the existing inline SVG
73 | this.resetInlineSVG()
74 |
75 | // Get the content of the SVG file
76 | const request = new XMLHttpRequest()
77 | request.open('GET', this.src, true)
78 | request.onload = function () {
79 | if (request.status >= 200 && request.status < 400) {
80 | // Setup a DOM parser to convert the response to text/xml
81 | const domParser = new DOMParser()
82 | const result = domParser.parseFromString(request.responseText, 'text/xml')
83 | const loadedSVG = result.getElementsByTagName('svg')[0]
84 |
85 | if (!loadedSVG) {
86 | console.error('No SVG element found in the given file: ' + context.filepath)
87 | return
88 | }
89 |
90 | // add attributes to the inline SVG
91 | const attributeNames = loadedSVG.getAttributeNames()
92 | for (const name of attributeNames) {
93 | const value = loadedSVG.getAttribute(name)
94 | context.$el.setAttribute(name, value)
95 | }
96 | if (context.customId) context.$el.id = context.customId
97 | if (context.customClassName) context.$el.setAttribute('class', context.customClassName)
98 | context.$el.style.width = context.width
99 | context.$el.style.height = context.height
100 |
101 | // add child nodes to the inline SVG
102 | const domN = loadedSVG.children.length;
103 | for (let i = domN - 1; i >= 0; i--) {
104 | const node = loadedSVG.children.item(i)
105 | context.$el.appendChild(node)
106 | }
107 |
108 | // set colors
109 | context.updateFillColor(context.fill)
110 | context.updateStrokeColor(context.stroke)
111 |
112 | // now the inline SVG is generated
113 | context.$emit('load')
114 | } else {
115 | console.error('There was an error retrieving the source of the SVG.')
116 | }
117 | }
118 |
119 | request.onerror = function () {
120 | console.error('There was on XML Http Request')
121 | }
122 |
123 | request.send()
124 | },
125 | resetInlineSVG () {
126 | while (this.$el.firstChild) {
127 | this.$el.removeChild(this.$el.firstChild)
128 | }
129 | const attributeNames = this.$el.getAttributeNames()
130 | for (const name of attributeNames) {
131 | this.$el.removeAttribute(name)
132 | }
133 | },
134 | updateFillColor (fill) {
135 | if (this.fillClassName) {
136 | const matches = this.$el.getElementsByClassName(this.fillClassName)
137 | for (const element of matches) {
138 | element.style.fill = fill
139 | }
140 | }
141 | },
142 | updateStrokeColor (stroke) {
143 | if (this.strokeClassName) {
144 | const matches = this.$el.getElementsByClassName(this.strokeClassName)
145 | for (const element of matches) {
146 | element.style.stroke = stroke
147 | }
148 | }
149 | }
150 | }
151 | }
152 |
153 | const plugin = {
154 | install (Vue, options) {
155 | Vue.component('simple-svg', SimpleSVG)
156 | }
157 | }
158 |
159 | export {plugin as default, SimpleSVG}
160 |
--------------------------------------------------------------------------------