@incrowd/widget-components

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

Downloads in past

Stats

StarsIssuesVersionUpdatedCreatedSize
@incrowd/widget-components
1.10.26 years ago6 years agoMinified + gzip package size for @incrowd/widget-components in KB

Readme

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:
  • Inside /static/img/ if using Nuxt.js or /public/img, require images:
- menu-icon.png if using HeaderBar and there is a menu page. - profile-picture.png and profile-form-pen-icon.png if using ProfileForm - white-cross.png if using ShareOverlay - profile-picture.png if using LeaderboardBody or LeaderboardFooter - 6 icons (leaderboard-star-0 to leaderboard-star-5) if using LeaderboardBody or LeaderboardFooter - 7 images (streak-0 to streak-30) if using LeaderboardBody or LeaderboardFooter - info-icon.png if using LeaderboardBody and need to display additional information about points ```css .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);
} ``` ```css .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

```html 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>
```

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: ```javascript 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: ```css .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

```html
<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>
```

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: ```css .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

```html
...
<profile-form
:profilePicture.sync="profilePicture"
:screenName.sync="screenName"
:firstName.sync="firstName"
:lastName.sync="lastName"
labelColor="#0054A5"
penIconBgColor="#299934"/>
...
```

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

```html
...
<share-overlay
:showShare.sync="showShare"
title="Share Title"
:platforms="platforms"
:trackShare="trackShare"
globalShareUrl="URL"/>
...
```

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: ```javascript 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: ```javascript 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

```html
...
<leaderboard-body
:rankings="rankings"
:handleScroll="handleScroll"
:pointInfo="pointInfo"
:btnObj="btnObj"
type="round"
typeValue="1"/>
...
```

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

```html ```

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.