0

I have a vue.js component (vue polygon cropper) under src/App.vue like below:

    <template>
        <div id="app">
            <h1 class="mb-4 mt-2 alert-success">Vue Image Cropper</h1>
            <div>
                <b-img :src="resultImage" alt="Responsive image" fluid v-show="showResult"></b-img>
                <div v-show="showResult">
                    <b-link :href="resultImage" download="result.png">Download Image</b-link>
                </div>
            </div>
            <div>
                <polygonCrop :canvasClass="'some-class'"
                             :height="600"
                             :imageSource="imgSrc"
                             :showCanvas="show"
                             :showPointer="showPointer"
                             :width="800"
                             ref="canvas"
                ></polygonCrop>
            </div>
            <b-row>
                <b-col>
                    <b-form-group>
                        <b-form-file
                                :state="Boolean(file)"
                                @change="setImage"
                                accept="image/*"
                                class="col-sm-6 mt-2"
                                drop-placeholder="Drop file here..."
                                placeholder="Choose a file or drop it here..."
                                size="lg"
                                v-model="file"
                        ></b-form-file>
                        <div class="mt-3">Selected file: {{ file ? file.name : '' }}</div>
                    </b-form-group>
    
                    <b-button @click.prevent="crop" variant="success">Crop</b-button>
                    <b-button @click.prevent="undo" variant="warning">Undo</b-button>
                    <b-button @click.prevent="redo" variant="primary">Redo</b-button>
                    <b-button @click.prevent="reset" variant="danger">Reset</b-button>
                </b-col>
            </b-row>
        </div>
    </template>
    <script>
        // import polygonCrop from '../../dist/PolygonCropper.umd';
        import polygonCrop from 'vue-polygon-cropper';
    
        export default {
            name: 'App',
            data() {
                return {
                    imgSrc: '/demo.png',
                    file: null,
                    show: false,
                    showResult: false,
                    showPointer: true,
                    resultImage: ""
                };
            },
            components: {
                polygonCrop
            },
            methods: {
                setImage(e) {
                    const file = e.target.files[0];
                    if (!file && file.type.indexOf('image/') === -1) {
                        alert('Please select an image file');
                        return;
                    }
                    if (typeof FileReader === 'function') {
                        const reader = new FileReader();
                        reader.onload = (event) => {
                            this.imgSrc = event.target.result;
                            this.show = true;
                        };
                        reader.readAsDataURL(file);
                    } else {
                        alert('Sorry, FileReader API not supported');
                    }
                },
                crop: function () {
                    this.$refs.canvas.crop();
                    this.resultImage = this.$refs.canvas.resultImage;
                    this.show = false;
                    this.showResult = true;
                },
                undo: function () {
                    this.$refs.canvas.undo();
                },
                redo: function () {
                    this.$refs.canvas.redo();
                },
                reset: function () {
                    this.show = true;
                    this.showResult = false;
                    this.$refs.canvas.reset();
                }
            }
        };
    </script>
    
    <style>
        #app {
            font-family: Avenir, Helvetica, Arial, sans-serif;
            -webkit-font-smoothing: antialiased;
            -moz-osx-font-smoothing: grayscale;
            text-align: center;
            color: #2c3e50;
            margin-top: 60px;
        }
    
        .some-class {
            border: 1px solid #2c3e50;
        }
    </style>

My main.js under src directory is as follows:

    import Vue from 'vue';
    import App from './App.vue';
    import {BootstrapVue, IconsPlugin} from 'bootstrap-vue';
    import 'bootstrap/dist/css/bootstrap.css';
    import 'bootstrap-vue/dist/bootstrap-vue.css';
    
    Vue.config.productionTip = false;
    Vue.use(BootstrapVue);
    Vue.use(IconsPlugin);
    
    new Vue({
        render: h => h(App),
    }).$mount('#app');

I need to do to convert this to web component, so I have done some changes in vue.config.js file to use the correct CSS styling:

    function enableShadowCss(config) {
      const configs = [
        config.module.rule('vue').use('vue-loader'),
        config.module.rule('css').oneOf('vue-modules').use('vue-style-loader'),
        config.module.rule('css').oneOf('vue').use('vue-style-loader'),
        config.module.rule('css').oneOf('normal-modules').use('vue-style-loader'),
        config.module.rule('css').oneOf('normal').use('vue-style-loader'),
        config.module.rule('postcss').oneOf('vue-modules').use('vue-style-loader'),
        config.module.rule('postcss').oneOf('vue').use('vue-style-loader'),
        config.module.rule('postcss').oneOf('normal-modules').use('vue-style-loader'),
        config.module.rule('postcss').oneOf('normal').use('vue-style-loader'),
        config.module.rule('scss').oneOf('vue-modules').use('vue-style-loader'),
        config.module.rule('scss').oneOf('vue').use('vue-style-loader'),
        config.module.rule('scss').oneOf('normal-modules').use('vue-style-loader'),
        config.module.rule('scss').oneOf('normal').use('vue-style-loader'),
        config.module.rule('sass').oneOf('vue-modules').use('vue-style-loader'),
        config.module.rule('sass').oneOf('vue').use('vue-style-loader'),
        config.module.rule('sass').oneOf('normal-modules').use('vue-style-loader'),
        config.module.rule('sass').oneOf('normal').use('vue-style-loader'),
        config.module.rule('less').oneOf('vue-modules').use('vue-style-loader'),
        config.module.rule('less').oneOf('normal-modules').use('vue-style-loader'),
        config.module.rule('stylus').oneOf('vue').use('vue-style-loader'),
        config.module.rule('stylus').oneOf('normal-modules').use('vue-style-loader'),
      ];
      configs.forEach(c => c.tap(options => {
        options.shadowMode = true;
        return options;
      }));
    }
    
    module.exports = {
      // https://cli.vuejs.org/guide/webpack.html#chaining-advanced
      chainWebpack: config => {
        enableShadowCss(config);
      }
    }

The problem is that when I generate a web component:

npm run build -- --target wc --name app-1

And it generates the web component inside dist folder as follows, but when I go to demo.html, it doesn't render CSS correctly, and the component doesn't show up correctly:

C:\ThermoAnalyser\vue_js\project1\dist>dir
 Volume in drive C is Windows-SSD
 Volume Serial Number is 18EE-B4F6

 Directory of C:\ThermoAnalyser\vue_js\project1\dist

27/12/2020  11:24 PM    <DIR>          .
27/12/2020  11:24 PM    <DIR>          ..
27/12/2020  11:24 PM            46,521 app-1.js
27/12/2020  11:24 PM            55,715 app-1.js.map
27/12/2020  11:24 PM            18,236 app-1.min.js
27/12/2020  11:24 PM            71,872 app-1.min.js.map
27/12/2020  11:24 PM               149 demo.html
               5 File(s)        192,493 bytes
               2 Dir(s)  300,479,696,896 bytes free

Also when I run:

npm run build

It is giving me this error:

-  Building for production... ERROR  TypeError: Cannot set property 'shadowMode' of undefined
TypeError: Cannot set property 'shadowMode' of undefined
    at C:\ThermoAnalyser\vue_js\project1\vue.config.js:26:24
    at Object.tap (C:\ThermoAnalyser\vue_js\project1\node_modules\webpack-chain\src\Use.js:14:20)
    at C:\ThermoAnalyser\vue_js\project1\vue.config.js:25:26
    at Array.forEach (<anonymous>)
    at enableShadowCss (C:\ThermoAnalyser\vue_js\project1\vue.config.js:25:11)
    at chainWebpack (C:\ThermoAnalyser\vue_js\project1\vue.config.js:34:5)
    at C:\ThermoAnalyser\vue_js\project1\node_modules\@vue\cli-service\lib\Service.js:236:40

I would greatly appreciate if someone help to generate the web component of my codes with correct CSS styling?

1 Answer 1

1

The enableShadowCss() code you have in vue.config.js is only needed for development mode. shadowMode is already enabled in the production build, so that isn't going to help you in your build process, and you should remove it.

The BootstrapVue components are not initialized in your main app, so you'll see error messages in the browser console, indicating unknown components.

To enable the third party components, you can initialize BootstrapVue in your target component to be exported (src/App.vue in this case):

<script>
import {BootstrapVue, IconsPlugin} from 'bootstrap-vue';

Vue.config.productionTip = false;
Vue.use(BootstrapVue);
Vue.use(IconsPlugin);

export default {
  //...
}
</script>

In addition, you'll need to import the styles in the target component's <style> block so that they're included in the component's Shadow CSS:

<style>
@import '~bootstrap/dist/css/bootstrap.css';
@import '~bootstrap-vue/dist/bootstrap-vue.css';

/*...*/
</style>

GitHub PR

Sign up to request clarification or add additional context in comments.

1 Comment

Hi Tony, thanks for your codes and amendment. However, when I try to build the project, it doesn't correctly load images into the canvas. I am not sure what is wrong in codes?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.