Разрабатываем переиспользуемый web-компонент для лайков
В рамках цикла статей о веб-компонентах мы рассмотрим пример реализации компонента лайков wc-likes, пройдемся по шагам реализации и интегрируем лайки прямо на webislife 😄 поехали!)
wc-like будет автономным пользовательским элементом – «полностью новый» элемент, расширяющий абстрактный класс HTMLElement
Функциональная часть
Что такое компонент лайка? это некий HTMLElement который обычно нам показывает число лайков и иконку с сердечком. Если бы мы его верстали, получалась бы +- такая emmet конструкция wc-likes>button+span Начнем с описания типов и конструктора нашего компонента
public Liked:boolean = false
public LikesRendered:boolean = false
public LikesCount:number = 0
public LikesFetch:boolean = false
public LikesCountNode:Node
public LikesIcon:HTMLElement
public LikesPhrases:WCLikesLang
public LikesPhrasesLang:WCLikesPhrasesKey|string
constructor() {
super();
const defaultLang = 'ru';
this.LikesCount = Number(this.getAttribute('value'));
this.LikesPhrasesLang = defaultLang;
this.LikesPhrases = WCLikesLangs[this.LikesPhrasesLang];
this.classList.add('wc-likes');
}
Вроде все понятно, добавляем себе возможность для поддержки мультиязычности и навешиваем класс wc-likes на элемент чтобы каждый раз его не писать в HTML
/**
* Создаем основные node элементы для нашего компонента лайков
*/
buildElement() {
//Кнопка с иконкой
this.LikesIcon = document.createElement('button');
//Like SVG icon - иконку можно поменять на свою, я вставил прямо в код
this.LikesIcon.innerHTML = '';
//Добавим слушателя события нажатия на кнопку
this.LikesIcon.onpointerup = ev => this.onLikesSubmit(ev);
//Создадим textNode с количеством лайков
this.LikesCountNode = document.createTextNode(`${this.LikesCount}`);
//Добавим элементы в DOM нашего компонента
this.append(this.LikesIcon);
this.append(this.LikesCountNode);
}
Рассмотрим метод обработки отправки нашего лайка
/**
* Логика обработки отправки лайка
* @param ev
*/
async onLikesSubmit(ev:Event) {{
//Если уже лайкнули
if(this.Liked) {
this.setAttribute('data-hint', this.LikesPhrases.alreadyLike);
return;
}
this.setAttribute('fetch', '1');
const fetchLikesStatus = await this.fetchSubmitLikes().catch(err => {
this.setAttribute('data-hint', err.toString());
});
this.removeAttribute('fetch');
switch (fetchLikesStatus) {
case 200:
const newCount = this.LikesCount + 1;
this.setAttribute('value', String(newCount));
this.setAttribute('liked', String(1));
break;
default:
this.setAttribute('data-hint', this.LikesPhrases.likeError);
break;
}
}};
Ничего сложного, мы проверяем лайкал ли уже пользователь и если нет, отправляем запрос в API и навешиваем -fetch класс CSS
Но на самом деле нам осталось реализовать еще 2 метода отправки и получения количества лайков, эта реализация зависит от сайта и его системы и не входит в философию веб-компонента, я расскажу про интеграцию лайков к постам на webislife.ru в следующей статье)
/**
* ABSTRACT METHOD NEEDS YOUR IMPLEMENTATION SEE wc-likes-post.ts example
* submitLike to backend
*/
async fetchSubmitLikes():Promise {
const PromiseAPIStatus:number = await new Promise((resolve, reject) => {
setTimeout(() => resolve(200), 1000);
});
return PromiseAPIStatus;
}
/**
* ABSTRACT METHOD NEEDS YOUR IMPLEMENTATION SEE wc-likes-post.ts example
* fetch likes count from backend
*/
async fetchAsyncLikes(params:any):Promise {
const PromiseAPIStatus:number = await new Promise((resolve, reject) => {
setTimeout(() => resolve(200), 1000);
});
return PromiseAPIStatus;
}
Два метода, которые осталось реализовать)
А ну и конечно же добавим немного SCSS стилей и CSS анимаций) куда же без этого
SCSS стили для компонента
@keyframes wcLikesSubmit {
0% {
visibility: visible;
top: 0;
opacity: 1;
}
100% {
opacity: 0;
top: -100px;
font-size: 4em;
}
}
@keyframes wcLikesFetch {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.wc-likes {
display: inline-flex;
align-items: center;
padding: 0px 1em 0px 0px;
border:1px solid var(--color-blue-light-100);
border-radius: 20px;
transition: all 0.2s ease;
user-select:none;
&:hover {
background-color: var(--color-blue-light-50);
border-color: var(--color-blue-light-50);
}
&:after {
min-width: 100px;
text-align: center;
white-space: nowrap;
background-color: var(--color-blue-light-50);
color: var(--color-blue-500);
}
& > button {
user-select: contain;
display: inline-flex;
align-items: center;
justify-content: center;
position: relative;
width: 2em;
height: 2em;
transition: all 0.2s ease;
margin-right: 10px;
color: var(--color-red-500);
background-color: #fff;
border: none;
border-radius: 50%;
outline: none;
cursor: pointer;
&:hover {
background-color: var(--color-red-100);
border: none;
& > svg {
fill: var(--color-red-500);
}
}
&:after {
visibility: hidden;
color: var(--color-red-500);
content: '♥';
position: absolute;
}
& > svg {
fill: var(--color-red-100);
min-width: 16px;
}
}
//fetch like state
&.-fetch {
opacity: 0.8;
pointer-events: none;
& > button:before {
position:absolute;
border: 2px solid var(--color-red-100);
border-top: 2px solid var(--color-red-500);
border-radius: 50%;
display: block;
width: 2em;
height: 2em;
content: '';
animation: wcLikesFetch 1s linear infinite;
}
}
//liked state
&.-liked {
background-color: var(--color-blue-light-100);
border-color: var(--color-blue-light-100);
& > button {
background-color: var(--color-red-500);
color: #fff;
&:after {
animation-fill-mode: forwards;
animation-duration: 0.4s;
animation-name: wcLikesSubmit;
}
& > svg {
fill: #fff;
stroke: none;
}
}
}
}
Веб компоненты — это замена современных фреймворков?
Однозначно нет — есть конечно представители фронтенда с перегибами в сторону нативных компонентов, как и есть армии фанатов react/vue/angular но у всех этих фреймворков уже есть поддержка web компонентов
- Vue и web компоненты
- web компоненты вместе React
- angular guide elements руководство на 🇺🇸
Для тех кому хочется побыстрее попробовать, уже готовый репозиторий на github и готовый npm пакет npm-wc-likes также пакет доступен на github.
Последняя редакция 31 января, 2023 в 03:01