Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Custom stencil implementation using Lightning Web Component. Two sample stencil structure provided as templates. Supported types are feed and list.
<template>
<!-- Embed stencil component in your parent component -->
<c-stencil iterations="4" type="feed" columns="2"></c-stencil>
<c-stencil iterations="4" columns="2"></c-stencil>
</template>
import { LightningElement } from 'lwc';
export default class App extends LightningElement {
}
<template>
<!--Leave this template blank - Contents will be rendered dynamically-->
</template>
/*
* @File Name : stencil.js
* @Description : Common JS for all HTML templates
* @Author : Akshay Poddar
* @Last Modified On : 22/6/2020, 8:02:44 pm
**/
import { LightningElement, api } from 'lwc';
import feedType from './stencilFeedType.html';
import listType from './stencilListType.html';
export default class Stencil extends LightningElement {
/* Exposed properties */
@api type = 'list'; //Type of stencil to render
@api iterations = 1; //Number of stencil items to display (Rows)
@api childClass = ''; //Add custom styling to each stencil item
@api columns = 1; //Number of columns in which iterations should be rendered
/* Private properties */
//default configuration object for text type skeleton - Change sizing from available classes in css file
listWrap = {
key: 1,
lines: [
{
key: 1,
class: 'text text-primary text-short text-thinnest'
},
{
key: 2,
class: 'text text-longer text-thinnest'
},
{
key: 3,
class: 'text text-long text-thinnest'
}
]
};
//default configuration object for feed type skeleton - Change sizing from available classes in css file
feedWrap = {
key: 1,
lines: [
{
key: 1,
class: 'text text-long text-thinnest'
},
{
key: 2,
class: 'text text-short text-thinnest'
}
],
isActionAvailable: true,
isIconAvailable: true,
isHeaderImageAvailable: false
};
//Conditionally render stencil type based on specified type (Add more stencil templates here)
render(){
switch(`${this.type}`.toLocaleLowerCase()){
case 'feed':
return feedType;
break;
case 'list':
default :
return listType;
break;
}
}
//Get list of elements iterated in list - convert count to list
get getIterationsForList(){
let list = [];
if(this.iterations<=1){
list.push({...this.listWrap});
}else{
for (let i = 0; i < this.iterations; i++) {
let temp = {...this.listWrap};
temp.key = i;
list.push(temp);
}
}
return list;
}
//Get feed elements iterated in list - convert count to list
get getIterationsForFeed(){
let list = [];
if(this.iterations<=1){
list.push({...this.feedWrap});
}else{
for (let i = 0; i < this.iterations; i++) {
let temp = {...this.feedWrap};
temp.key = i;
list.push(temp);
}
}
return list;
}
//Calculates division of columns for dynamic grid
get viewPortSize() {
return Math.ceil(12/this.columns);
}
//change or update style class for each element in list (Example: Apply custom margin around each stencil card)
get getChildClass(){
return `${this.childClass}`?`${this.childClass}`:'slds-var-m-around_xx-small';
}
}
/* render() methhod require to reference CSS from an extra template,
the CSS filename must match the filename of the extra template */
.placeholder .body {
padding: 1.5rem
}
.placeholder .icon {
width: 2rem;
height: 2rem;
background-color: #ecebea;
border-radius: .25rem;
margin-right: .75rem;
display: inline-block
}
.placeholder .icon-large {
width: 3rem;
height: 3rem;
margin: 0px 10px 10px 0px;
}
.placeholder .icon-medium {
width: 2rem;
height: 2rem;
margin: 0px 10px 10px 0px;
}
.placeholder .icon-round-large {
width: 3rem;
height: 3rem;
border-radius: 50%;
margin: 0px 10px 10px 0px;
}
.placeholder .text-body {
margin-bottom: 10px;
width: 70%;
height: 100%
}
.placeholder .text {
border-radius: 15rem;
display: block;
margin-bottom: .75rem;
background-color: #ecebea;
height: .5rem
}
.placeholder .text:last-child {
margin-bottom: 0
}
.placeholder .text-thin {
height: 6px;
border-radius: 15rem
}
.placeholder .text-thinnest {
height: 10px;
border-radius: 15rem
}
.placeholder .text-thinner {
height: 4px;
margin: 10px 0;
border-radius: .125rem
}
.placeholder .text-long {
width: 80%
}
.placeholder .text-longer {
width: 100%
}
.placeholder .text-medium {
width: 60%
}
.placeholder .text-short {
width: 45%
}
.placeholder .text-shorter {
width: 20%
}
.placeholder .body,
.placeholder .card {
background-color: #fff;
}
.placeholder .icon,
.placeholder .custom-header-image,
.placeholder .text {
background-color: #ecebea;
background-image: linear-gradient(90deg, #ecebea 0px, #d1d1d1 40px, #ecebea 100px) ;
background-size: 900px;
animation: shine-lines 1s infinite linear;
}
.placeholder .card {
border: 1px solid #f3f2f2;
border-radius: .25rem
}
.placeholder .card .custom-header-image {
background-position: center;
background-size: auto;
border-radius: 5px 5px 0 0;
width:100%;
height:145px;
background-color: #ecebea;
margin-bottom: 12px
}
.placeholder .flex-grid {
display: flex
}
.placeholder .flex-h-center {
justify-content: center
}
.placeholder .flex-v-center {
align-items: center;
}
.placeholder.detail {
background-color: #fff;
border-radius: 0.25rem;
}
.placeholder.detail .right-column {
display: block
}
.placeholder.detail .field {
padding: 1rem 1rem
}
.placeholder.detail .text-secondary {
background-color: #ecebea
}
.placeholder.detail .field:nth-child(even) .text-secondary {
width: 33%
}
.placeholder.feed {
height: 100%;
width: 100%;
}
.placeholder.feed .card {
width: 100%;
padding: 1rem;
border-radius: .25rem
}
.placeholder.feed .card .text {
margin: 12px 0px 0px;
}
.placeholder.feed .card .text-body:nth-child(odd) {
width: 100%
}
.placeholder.feed .card .actions {
border-top: 1px solid #f3f2f2;
padding: 1rem 0 0.5rem
}
.placeholder.feed .card .actions .text {
float: left;
margin: 0
}
.placeholder.feed .card .actions .text:last-child {
float: right
}
.placeholder.feed .card:nth-child(even) .text-shorter {
width: 15%
}
@keyframes shine-lines{
0% {
background-position: -20px
}
100%{
background-position: 400px
}
}
<!--
@File Name : stencilFeedType.html
@Description : HTML templates for feed type of skeleton structure
@Author : Akshay Poddar
@Last Modified On : 22/6/2020, 8:02:44 pm
-->
<template>
<lightning-layout multiple-rows="true">
<template for:each={getIterationsForFeed} for:item="iteration" >
<lightning-layout-item key={iteration.key} size="12" small-device-size={viewPortSize} medium-device-size={viewPortSize} large-device-size={viewPortSize}>
<div class={getChildClass}>
<div class="placeholder feed">
<!-- Design Skeleton -->
<div class="card">
<div if:true={iteration.isHeaderImageAvailable} class="custom-header-image"></div>
<div class="flex-grid flex-v-center">
<div if:true={iteration.isIconAvailable} class="icon icon-round-large"></div>
<div class="text-body">
<div class="text text-short text-thinnest" style="margin: 0;"></div>
<div class="text text-thinnest" ></div>
</div>
</div>
<div class="text-body">
<template for:each={iteration.lines} for:item="line">
<div key={line.key} class={line.class}></div>
</template>
</div>
<div if:true={iteration.isActionAvailable} class="actions">
<div class="text text-shorter text-thin"></div>
<div class="text text-shorter text-thin"></div>
</div>
</div>
</div>
</div>
</lightning-layout-item>
</template>
</lightning-layout>
</template>
/* render() methhod require to reference CSS from an extra template,
the CSS filename must match the filename of the extra template */
.placeholder .body {
padding: 1.5rem
}
.placeholder .icon {
width: 2rem;
height: 2rem;
background-color: #ecebea;
border-radius: .25rem;
margin-right: .75rem;
display: inline-block
}
.placeholder .icon-large {
width: 3rem;
height: 3rem;
margin: 0px 10px 10px 0px;
}
.placeholder .icon-medium {
width: 2rem;
height: 2rem;
margin: 0px 10px 10px 0px;
}
.placeholder .icon-round-large {
width: 3rem;
height: 3rem;
border-radius: 50%;
margin: 0px 10px 10px 0px;
}
.placeholder .text-body {
margin-bottom: 10px;
width: 70%;
height: 100%
}
.placeholder .text {
border-radius: 15rem;
display: block;
margin-bottom: .75rem;
background-color: #ecebea;
height: .5rem
}
.placeholder .text:last-child {
margin-bottom: 0
}
.placeholder .text-thin {
height: 6px;
border-radius: 15rem
}
.placeholder .text-thinnest {
height: 10px;
border-radius: 15rem
}
.placeholder .text-thinner {
height: 4px;
margin: 10px 0;
border-radius: .125rem
}
.placeholder .text-long {
width: 80%
}
.placeholder .text-longer {
width: 100%
}
.placeholder .text-medium {
width: 60%
}
.placeholder .text-short {
width: 45%
}
.placeholder .text-shorter {
width: 20%
}
.placeholder .body,
.placeholder .card {
background-color: #fff;
}
.placeholder .icon,
.placeholder .custom-header-image,
.placeholder .text {
background-color: #ecebea;
background-image: linear-gradient(90deg, #ecebea 0px, #d1d1d1 40px, #ecebea 100px) ;
background-size: 900px;
animation: shine-lines 1s infinite linear;
}
.placeholder .card {
border: 1px solid #f3f2f2;
border-radius: .25rem
}
.placeholder .card .custom-header-image {
background-position: center;
background-size: auto;
border-radius: 5px 5px 0 0;
width:100%;
height:145px;
background-color: #ecebea;
margin-bottom: 12px
}
.placeholder .flex-grid {
display: flex
}
.placeholder .flex-h-center {
justify-content: center
}
.placeholder .flex-v-center {
align-items: center;
}
.placeholder.detail {
background-color: #fff;
border-radius: 0.25rem;
}
.placeholder.detail .right-column {
display: block
}
.placeholder.detail .field {
padding: 1rem 1rem
}
.placeholder.detail .text-secondary {
background-color: #ecebea
}
.placeholder.detail .field:nth-child(even) .text-secondary {
width: 33%
}
.placeholder.feed {
height: 100%;
width: 100%;
}
.placeholder.feed .card {
width: 100%;
padding: 1rem;
border-radius: .25rem
}
.placeholder.feed .card .text {
margin: 12px 0px 0px;
}
.placeholder.feed .card .text-body:nth-child(odd) {
width: 100%
}
.placeholder.feed .card .actions {
border-top: 1px solid #f3f2f2;
padding: 1rem 0 0.5rem
}
.placeholder.feed .card .actions .text {
float: left;
margin: 0
}
.placeholder.feed .card .actions .text:last-child {
float: right
}
.placeholder.feed .card:nth-child(even) .text-shorter {
width: 15%
}
@keyframes shine-lines{
0% {
background-position: -20px
}
100%{
background-position: 400px
}
}
<!--
@File Name : stencilListType.html
@Description : HTML templates for list type of skeleton structure
@Author : Akshay Poddar
@Last Modified On : 22/6/2020, 8:02:44 pm
-->
<template>
<lightning-layout multiple-rows="true">
<template for:each={getIterationsForList} for:item="iteration" >
<lightning-layout-item key={iteration.key} size="12" small-device-size={viewPortSize} medium-device-size={viewPortSize} large-device-size={viewPortSize}>
<div class={getChildClass}>
<!-- Design Skeleton -->
<div class="placeholder activity">
<div class="placeholder detail">
<div class="left-column">
<div class="field">
<template for:each={iteration.lines} for:item="line">
<div key={line.key} class={line.class}></div>
</template>
</div>
</div>
</div>
</div>
</div>
</lightning-layout-item>
</template>
</lightning-layout>
</template>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.