@incrowd/widget-components

    1.10.2 • Public • Published

    Widget Components

    A collection of reusable Vue.js components for web widgets.

    Motivation

    To reuse widget components across all web widget projects.

    Requirements

    The consuming project must be a Vue.js project with the following:

    .vb > .vb-dragger {
        z-index: 5;
        width: 12px;
        right: 0;
    }
     
    .vb > .vb-dragger > .vb-dragger-styler {
        -webkit-backface-visibility: hidden;
        backface-visibility: hidden;
        -webkit-transform: rotate3d(0,0,0,0);
        transform: rotate3d(0,0,0,0);
        -webkit-transition: 
            background-color 100ms ease-out,
            margin 100ms ease-out,
            height 100ms ease-out;
        transition: 
            background-color 100ms ease-out,
            margin 100ms ease-out,
            height 100ms ease-out;
        background-color: rgba(150, 150, 150,.1);
        margin: 5px 5px 5px 0;
        border-radius: 20px;
        height: calc(100% - 10px);
        display: block;
    }
     
    .vb.vb-scrolling-phantom > .vb-dragger > .vb-dragger-styler {
        background-color: rgba(150, 150, 150,.3);
    }
     
    .vb > .vb-dragger:hover > .vb-dragger-styler {
        background-color: rgba(150, 150, 150,.5);
        margin: 0px;
        height: 100%;
    }
     
    .vb.vb-dragging > .vb-dragger > .vb-dragger-styler {
        background-color: rgba(150, 150, 150,.5);
        margin: 0px;
        height: 100%;
    }
     
    .vb.vb-dragging-phantom > .vb-dragger > .vb-dragger-styler {
        background-color: rgba(150, 150, 150,.5);
    }
    .lb-table {
        width: 100%;
        border-collapse: collapse;
    }
     
    .lb-footer .lb-table {
        margin: auto 0;
    }
     
    .lb-head {
        text-transform: uppercase;
        text-align: center;
        color: #97A0A5;
        background-color: #F1F5F7;
        font-size: 80%;
    }
     
    .lb-head-row {
        height: 2.4rem;
    }
     
    .lb-point-info-icon {
        cursor: pointer;
        height: 1rem;
        margin-bottom: 0.25rem;
    }
     
    .lb-body-row {
        border-bottom: 1px solid #dee2e6;
        color: #232F43;
    }
     
    .lb-pundit-row {
        background: linear-gradient(to right#FFA548 , #FAD961);
    }
     
    .lb-pundit-row .lb-body-cell {
        color: #fff;
    }
     
    .lb-rank-cell.lb-streak-cell.lb-btn-cell {
        width: 4rem;
        text-align: center;
    }
     
    .lb-rank-cell-label {
        height: 2rem;
        width: 2rem;
        padding-top: 0.25rem;
        margin-bottom: 0;
        font-size: 1rem;
        font-weight: 700;
    }
     
    .lb-rank-cell-p {
        font-size: 1rem;
        font-weight: 700;
    }
     
    .lb-name-cell {
        width: 13rem;
        display: flex;
    }
     
    .lb-profile-picture {
        border-radius: 50%;
        background-color: #fff;
        border: 1px solid #dee2e6;
        margin: 0.25rem 0;
        height: 3.7rem;
        width: 3.7rem;
    }
     
    .lb-star-icon {
        position: absolute;
        min-height: 1.6rem;
        min-width: 1.6rem;
        right: -0.8rem;
        bottom: -0.8rem;
        border-radius: 50%;
        border: 0.1rem solid white;
    }
     
    .lb-name-cell-p-container {
        margin: auto 0;
        max-width: 14rem;
    }
     
    .lb-name-cell-p {
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        font-size: 1.2rem;
        font-weight: 700;
        margin: auto 0 auto 1.5rem;
    }
     
    .lb-name-cell-p-top-player {
        font-size: 1rem;
    }
     
    .lb-point-cell {
        width: 5rem;
        text-align: center;
    }
     
    .lb-point-cell.lb-streak-cell {
        font-size: 1.2rem;
        font-weight: 500;
    }
     
    .lb-streak-cell-flag {
        width: 2.2rem;
    }
     
    .lb-streak-cell-flag-p {
        position: absolute;
        width: 100%;
        bottom: 1.25rem;
    }
     
    .lb-btn-cell-btn {
        background-color: #299934;
        cursor: pointer;
        color: #fff;
        border-radius: 0;
        padding: 0.25rem 0.5rem;
        border: 1px solid transparent;
    }
     
    .lb-footer {
        height: 5rem;
        background-color: #232F43;
        color: #fff;
        display: flex;
        border-radius: 0.8rem 0.8rem 0 0;
    }
     
    .lb-footer .lb-profile-picture {
        margin-top: auto;
        margin-bottom: auto;
    }
     
    .lb-footer .lb-btn-cell-btn {
        background-color: #fff;
        color: #4a4a4a;
        border-radius: 0.25rem;
    }

    Getting Started

    npm i @incrowd/widget-components or yarn add @incrowd/widget-components

    then in main.js or a JavaScript file that serve as the entry point of the application:

    import '@incrowd/widget-components'

    It will automatically register all widget components.

    Usage

    GenericLayout

    <generic-layout
        fullBg="/path/to/background/image"
        fullBgColor="colour"
        bodyBg="/path/to/background/image"
        :hideAlert="false">
        <div slot="header">HEADER</div>
        <div slot="subHeader">SUBHEADER</div>
        <div slot="body">BODY</div>
        <div slot="footer">FOOTER</div>
    </generic-layout>

    Props

    Prop Type Default
    fullBg String
    fullBgColor String
    bodyBg String
    hideAlert Boolean false

    Slots

    header and subHeader slots will always be fixed at the top of the widget.

    body slot uses vuebar for scrollbar hence the CSS requirement to style it. body will fill up any space left inside the widget between headers and footer.

    footer slot will always be fixed at the bottom of the widget.

    Alert

    An alert box is included as part of the GenericLayout. To use it, it is required to have Vuex store set up and the store must contain the following default state and mutation:

    export const state = () => ({
        ...
        alert: null,
        ...
    })
     
    export const mutations = {
        ...
        setAlert (state, alert) {
            state.alert = alert
        },
        ...
    }

    To show an alert, e.g. call store.commit('setAlert', {msg: 'Something went wrong', type: 'error'}). The alert box will then have class of alert-${type}. By default text in alert box is white in colour, to style certain type of alert, add CSS in global scope, for example:

    .alert-error {
        background-color: #252525;
    }
    .alert-success {
        background-color: #299934;
    }

    By default, the text for dismissing the alert is OK, to replace it, call alert with btnText as part of the object: store.commit('setAlert', {msg: 'Something went wrong', type: 'error', btnText: 'Dismiss'})

    The alert will not be shown if hideAlert prop is set to true.


    HeaderBar

    <generic-layout>
        <header-bar slot="header"
            height="3"
            headerBg="/path/to/background/image"
            bgColor="colour"
            title="title"
            :isTitleWhite="true"
            :hasShadow="false"
            :hasMenu="false">
            <div slot="left">LEFT</div>
            <div slot="mid">MID</div>
            <div slot="right">RIGHT</div>
        </header-bar>
    </generic-layout>

    Props

    Prop Type Default
    height* String 5
    headerBg String
    bgColor String #002672
    hasBgShadow Boolean true
    title String
    isTitleWhite Boolean true
    titleClasses** Array
    hasShadow*** Boolean false
    hasMenu Boolean false
    midZIndex String 0

    * height in rem

    ** Array of CSS classes apply to title of header bar

    *** If hasShadow is true, class box-shadow will be applied, style it by adding CSS in global scope, for example:

    .box-shadow {
        box-shadow: 0 1px 5px 0 rgba(0,0,0,0.2);
    }

    Slots

    Elements inside left slot will be aligned to the left and elements inside right slot will be aligned to the right.

    If hasMenu is true, left slot will display menu-icon.png be default hence the requirement of having the image and clicking on it will navigate to /menu page.

    By default, text in mid slot is centred and will display title using <h3> with class header-bar-title.


    ProfileForm

    <template>
        ...
        <profile-form
            :profilePicture.sync="profilePicture"
            :screenName.sync="screenName"
            :firstName.sync="firstName"
            :lastName.sync="lastName"
            labelColor="#0054A5"
            penIconBgColor="#299934"/>
        ...
    </template>
     
    <script>
    export default {
        data: () => ({
            profilePicture: null,
            screenName: null,
            firstName: null,
            lastName: null
        })
    }
    </script> 

    Props

    Prop Type Required Default
    profilePicture Any Yes
    screenName Any Yes
    firstName Any Yes
    lastName Any Yes
    labelColor String No #707070
    penIconBgColor String No #000

    The profile form consist of four inputs: An image upload and three text fields for the names. The parent component must have data set up as shown above and pass them as props into ProfileForm with .sync. When the values change inside ProfileForm, the corresponding value in data in parent will be updated as well.

    Alert

    store.commit('setAlert', {msg: 'Image too big', type: 'error'}) will be called if profile image size is greater than 8388607.

    Style

    The three input fields for name have class form-control, add CSS in global scope to style them.

    Images

    profile-picture.png will be used as the placeholder image and profile-form-pen-icon.png is the pen icon next to the profile image.


    ShareOverlay

    <template>
        ...
        <share-overlay
            :showShare.sync="showShare"
            title="Share Title"
            :platforms="platforms"
            :trackShare="trackShare"
            globalShareUrl="URL"/>
        ...
    </template>
     
    <script>
    export default {
        methods: {
            trackShare (platform) {...}
        },
        data: () => ({
            showShare: false,
            platforms: [
                ...
                {
                    network: 'facebook',
                    url: 'SHARE_URL',
                    title: 'TITLE',
                    description: 'DESCRIPTION',
                    quote: 'QUOTE'
                }
                ...
            ]
        })
    }
    </script> 

    Props

    Prop Type Required
    showShare Boolean Yes
    title String No
    platforms Array Yes
    trackShare Function No
    globalShareUrl String No

    .sync is needed for showShare to enable ShareOverlay to update its value, there will be white-cross.png that will set showShare to false when clicked, effectively dismissing the overlay.

    title is the title of the overlay displayed above the platform icons.

    platforms is an array of objects, each object contains information about the social platform as shown above.

    trackShare will be called when a share dialog is opened.

    If globalShareUrl exists, it will be used instead of url within each platform.

    vue-social-sharing

    vue-social-sharing is used, please read the documentation and construct an appropriate platforms array. Please install NPM package vue-social-sharing then in main.js or a JavaScript file that serve as the entry point of the application, add:

    import Vue from 'vue'
    import SocialSharing from 'vue-social-sharing'
    Vue.use(SocialSharing)

    Currently ShareOverlay only support the following networks: facebook, twitter, linkedin, googleplus, whatsapp, sms, email, and only title, description, and quote are supported for share text.

    Font Awesome 5

    Font Awesome 5 icons are required. Inside the template of the component, it uses tag name fa for icons: <fa :icon="p.icon"/>. In a Nuxt.js project, please install NPM packages nuxt-fontawesome, @fortawesome/free-solid-svg-icons and @fortawesome/free-brands-svg-icons, then in nuxt.config.js add:

    modules: ['nuxt-fontawesome'],
    fontawesome: {
        component: 'fa',
        imports: [
            {set: '@fortawesome/free-solid-svg-icons'},
            {set: '@fortawesome/free-brands-svg-icons', icons: ['faFacebookSquare', 'faTwitterSquare', 'faGooglePlusSquare', 'faLinkedin', 'faWhatsappSquare]}
        ]
    }

    LeaderboardBody

    <template>
        ...
        <leaderboard-body
            :rankings="rankings"
            :handleScroll="handleScroll"
            :pointInfo="pointInfo"
            :btnObj="btnObj"
            type="round"
            typeValue="1"/>
        ...
    </template>
     
    <script>
    import _ from 'lodash'
    export default {
        computed: {
            btnObj () {
                return {
                    key: 'KEY',
                    text: 'BUTTON_TEXT',
                    action: () => { ... }
                }
            }
        },
        methods: {
            handleScroll: _.throttle(function (body) {
                // Your logic
            }, 1000)
        },
        data: () => ({
            rankings: {...},
            pointInfo: 'POINT_INFO'
        })
    }
    </script> 

    Props

    Prop Type Required Default
    rankings Any Yes
    handleScroll Function No
    pointInfo String No
    btnObj Object No
    type String No 'season'
    typeValue String No

    rankings should be an object containing the leaderboard and user objects. The leaderboard object should then contains the users array which will be used to render the leaderboard.

    handleScroll is a function that will run when user scroll on the leaderboard. It is recommended to use lodash throttle to limit calls per second.

    pointInfo is a string for displaying additional information about the points. If pointInfo exist, info-icon.png will be displayed next to points header and clicking on it will call store.commit('setAlert', {msg: pointInfo, type: 'info'}). Please read above for more information about alert, and add CSS class alert-info in global scope to style.

    If btnObj exist, an extra column of buttons will be added to the leaderboard. The key controls whether a user should have a button or not, a button will be shown if user[key] exist. btnObj.text is the text displayed on the button and btnObj.action will be called when the button is clicked.

    type can be season, month, or round.

    typeValue for season world normally be year such as 2018, round will be a number and so as month, e.g. 8 will be used for August.

    Style

    Add leaderboard.css in global scope and edit it when needed.

    Streak

    There is a column for streak and the streak images will be used depending on user's streak.


    LeaderboardFooter

    <leaderboard-footer :rankings="rankings" :btnObj="btnObj"/>

    Props

    Prop Type Required Default
    rankings Any Yes
    btnObj Object No
    type String No 'season'
    typeValue String No

    Same as LeaderboardBody except button won't check for key.

    Style

    Add leaderboard.css in global scope and edit it when needed.

    Keywords

    none

    Install

    npm i @incrowd/widget-components

    DownloadsWeekly Downloads

    4

    Version

    1.10.2

    License

    none

    Unpacked Size

    645 kB

    Total Files

    9

    Last publish

    Collaborators

    • saelora
    • horacekeung
    • josh_clement