Сайт визитка на Angular. Создание UI KIT.
В данной статье поговорим о создании UI KIT для приложения. Поговорим о том зачем он нужен, и приведем реализацию компонентов с использованием Angular Material.
Под UI KIT будем понимать набор компонентов из которых будет собираться приложение. Каждый элемент в UI KIT’е не привязан к конкретному контексту, то есть не зависит от конкретных данных. Если компонент и принимает данные, то скорее всего в виде предоставляемого компонентом интерфейсом.
Обычно при разработке приложения есть дизайнер, который формирует позиционирование бренда, вместе с этим разрабатывает и требования к дизайну.
Так как дизайнер отсутствует, будем считать, что Angular Material это лучшее решение.
Angular Material предоставляет стили для большинства UI элементов. Но есть часть компонентов, которые отсутствуют, в частности решение для задания сеток и карусель.
У Angular есть пакет Angular Flex-Layout, но лично меня он немного коробит, и я не рекомендую его использовать, так как перекладывать логику задания стилей с CSS на JS достаточно сомнительно.
Реализуем следующие компоненты и модули:
CarouselModule
— модуль, который будет создавать карусель из принимающих изображений.ContainerModule
— модуль, который будет создавать блок фиксированного размера и выравнивать его по центру. Модуль реализует класс контейнера из Twitter Bootstrap.GridModule
— модуль, который позволит создавать сетки отобращения элементов. Модуль реализует метод сеток из Twitter Bootstrap.ImageModule
— модуль, который будет содержать pipe для работы с изображениями.LayoutModule
— главный макет приложения, который будет включать в себя такие компоненты как шапка, подвал, sidebar и прочее.LinkModule
— модуль, который будет содержать директивы и пайпы для ссылок.MultiplatformModule
— модуль, который позволяет выводить разное содержимое для 3 платформ (handset, tablet и web).WidthModule
— модуль для манипуляции шириной элементов.
Создание темы Angular Material
Создадим новую папку stylesheets
в libs
. Добавим её в .nxignore
.
Создадим новый файл material-theme.scss
, в который переместим стили создания темы Angular Material:
Добавим файл с глобальными стилями global.scss
:
Для того, чтобы не зависеть от пакета normalize, украдем все стили и добавим их в файл normalize.scss
.
Создадим файл mixins.scss
, в котором сделаем короткие вызовы для адаптивных размеров :
И в конце создадим файл с глобальными переменными:
Добавим все это в styles.scss
:
// Global styles
@import 'libs/ui/stylesheets/normalize';
@import 'libs/ui/stylesheets/material-theme';
@import 'libs/ui/stylesheets/global';
Carousel Module
Реализуем простую карусель, где в компонент будет передаваться массив изображений, которые будут отображаться.
Так как нужен адаптивный компонент, для карусели нужно будет задавать фиксированную высоту.
Принцип работы следующий:
- Создается компонент карусели.
- В компоненте создаются кнопки навигации влево и вправо, а также точки, при клике на которые можно перейти к конкретному слайду.
- Далее все изображения выводятся в дочернем компоненте, со свойствами
{position: absolute; top: 0; left: 0; z-index: 1;}
. Это позволит наложить изображения друг на друга. Так как в карусели есть индекс активного слайда, для активного слайда устанавливается{ z-index: 2 }
. - Так как изображения могут быть разного размера, будет использоваться отображение с помощью CSS свойства
background-image
, вместо стандартного отображения с помощью тегаimage
.
Создадим библиотеку:
nx g lib ui/carousel
Реализуем компонент:
Компонент точек CarouselDotsModule
:
Как видно из реализации, по количеству слайдов создается массив точек.
export class CarouselDotsComponent implements OnInit {
@Input() counts!: number;
@Input() active!: number;
@Output() selected = new EventEmitter<number>();
dots!: number[];
ngOnInit(): void {
this.dots = this.counts ? new Array(this.counts) : [];
}
onSelect(index: number): void {
this.selected.emit(index);
}
}
При клике на точку, происходит событие selected
.
Компонент навигации по слайдам влево и право CarouselNavsModule
:
Компонент с навигацией еще проще.
export class CarouselNavsComponent {
@Output() prev = new EventEmitter<void>();
@Output() next = new EventEmitter<void>();
onPrev(): void {
this.prev.emit();
}
onNext(): void {
this.next.emit();
}
}
При клике соответствующих кнопок происходят события prev
или next
.
Модуль слайда выглядит следующим образом CarouselSlideModule
:
Компонент слайда принимает 2 параметра:
image
— путь до изображения;active
— является ли текущий слайд активным.
export class CarouselSlideComponent {
@Input() image!: string;
@Input() active = false;
@Output() clicked = new EventEmitter<void>();
onClick(): void {
this.clicked.emit();
}
}
При клике по слайду происходит вызов события clicked
.
Событие называется
clicked
не случайно. В документации Angular не совсем ясно написано, что все события которые создаются с помощью@Output
могут перекрывать стандартные события. Поэтому лучше все кастомные события называть так, чтобы они не совпадали со стандартными событиями DOM’а.
Пример работы карусели:
<banshop-carousel [images]="product.photos"></banshop-carousel>
Container Module
Цель компонента контейнера задать фиксированную ширину в зависимости от ширины экрана.
Создадим библиотеку:
nx g lib ui/container
Как видно из реализации, контейнер в зависимости от ширины экрана и ориентации получает необходимые размеры:
@use 'libs/ui/stylesheets/mixins' as store-mixins;
:host {
display: block;
padding-left: 1rem;
padding-right: 1rem;
margin-left: auto;
margin-right: auto;
width: 100%;
... &.is-fluid {
max-width: 100%;
}
@include store-mixins.media-tablet-portrait() {
max-width: 540px;
&.is-fluid {
max-width: 100%;
}
}
@include store-mixins.media-tablet-landscape() {
max-width: 900px;
&.is-fluid {
max-width: 100%;
}
}
@include store-mixins.media-web-portrait() {
max-width: 790px;
&.is-fluid {
max-width: 100%;
}
}
@include store-mixins.media-web-landscape() {
max-width: 1200px;
&.is-fluid {
max-width: 100%;
}
}
}
Также контейнер принимает свойство mode
, которое добавляет специфичные стили компоненту:
:host {
... &.is-flex {
display: flex;
flex-direction: column;
flex-wrap: wrap;
}
&.is-flex-row {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
}
&.is-flex-row-space {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: space-between;
align-items: center;
}
&.is-max-height {
height: 100%;
} ...
}
Пример работы контейнера:
<banshop-container [mode]="mode">
<router-outlet></router-outlet>
</banshop-container>
Grid Module
Реализация сетки достаточно тривиальна.
Процесс следующий:
- Создается два компонента
row
иcolumn
. - Далее создаются директивы размеров
handset
,tablet
иweb
. - Затем в зависимости от наличия директив, компонент принимает те или иные свойства.
Подробнее о создании сеток можно почитать здесь — Реализация колонок в Angular с использованием Angular CDK.
Создадим библиотеку:
nx g lib ui/grid
В результате получим модуль для строк RowModule
:
Модуль для колонок ColumnModule
:
Модули директив:
Модуль для сеток, который подключает в себя модули колонок, строк и директив GridModule
:
Пример работы сеток:
<banshop-row tablet>
<banshop-column tablet="6" web="4" *ngFor="let product of products;">
<banshop-product-card [product]="product"></banshop-product-card>
</banshop-column>
</banshop-row>
Image Module
Небольшой модуль для создания backgroundImage
.
Создадим библиотеку и добавим реализацию:
nx g lib ui/image
Пример использования:
<div [ngStyle]="image | backgroundImage"></div>
Link Module
Модуль для задания ссылкам определенных стилей.
Аналогично создадим библиотеку и добавим реализацию:
nx g lib ui/link
Так как Angular не дает задавать стили директивам, использование следующее — в файле стилей компонента импортируются следующие стили.
@import 'libs/ui/link/src/lib/link.directive';
Это необходимо, чтобы работала инкапсуляция стилей. В противном случае стили необходимо делать глобальными.
Multiplatform Module
Модуль, который позволяет выводить разное содержимое для 3 платформ: handset
, tablet
и web
.
Создадим библиотеку:
nx g lib ui/multiplatform
Реализация заключается в подписке на текущий размер с помощью LayoutService
.
В шаблоне компонента определяются разделы для вывода ng-content’а
в соответствии с требуемым размером.
<ng-container [ngSwitch]="layoutType$ | async">
<ng-container *ngSwitchCase="breakpoints.Web">
<ng-content select="[web]" automation-id="web"></ng-content>
</ng-container>
<ng-container *ngSwitchCase="breakpoints.Tablet">
<ng-content select="[tablet]" automation-id="tablet"></ng-content>
</ng-container>
<ng-container *ngSwitchDefault>
<ng-content select="[handset]" automation-id="handset"></ng-content>
</ng-container>
</ng-container>
Пример работы на карточке товара в корзине:
<banshop-multiplatform>
<ng-container handset> ... </ng-container>
<ng-container tablet> ... </ng-container>
<ng-container web> ... </ng-container>
</banshop-multiplatform>
Width Module
Модуль для задания ширины компонентам.
Создадим библиотеку:
nx g lib ui/width
В основном используется для задания
{width: 100% }
элементам формы Angular Material.
Layout Module
Добавим основной макет приложения.
Создадим библиотеку:
nx g lib ui/layout
Header
Сгенерируем шапку приложения:
nx g m header --project=ui-layout
nx g c header --project=ui-layout
Добавим Logo
:
Добавим кнопки для шапки:
В результате получим:
Footer
Создадим подвал приложения.
В результате получим:
Sidebar
Добавим боковую панель для tablet
.
Nav
Добавим меню для tablet
.
И меню для мобильной версии:
Тогда итоговый layout
примет вид:
Пример с условиями самый показательный:
Ссылки
Предыдущая статья — Создание core библиотек.
Следующая статья —Модуль товаров.
Все исходники находятся на github, в репозитории:
Для того, чтобы посмотреть состояние проекта на момент написания статьи, нужно выбрать соответствующий тег — article.
Подписывайтесь на блог, чтобы не пропустить новые статьи про Angular, и веб-разработку. Medium | Telegram| VK |Tw| Ln