Merge remote-tracking branch 'origin/main'

This commit is contained in:
cyber-dream 2022-11-23 03:53:21 +03:00
commit 1e7a347c83
28 changed files with 164 additions and 94 deletions

View File

@ -22,7 +22,7 @@
--local-size: 50px; --local-size: 50px;
} }
&[data-size="default"] { &[data-size="default"] {
--local-size: 42px; --local-size: 40px;
font-weight: 500; font-weight: 500;
} }
&[data-size="small"] { &[data-size="small"] {

View File

@ -1,3 +1,3 @@
<button [attr.data-appearance]="appearance"> <button [attr.data-appearance]="appearance" [attr.data-size]="size">
<ng-content></ng-content> <ng-content></ng-content>
</button> </button>

View File

@ -2,11 +2,9 @@
display: block; display: block;
button { button {
padding: var(--sk-gap-m) var(--sk-gap-xl);
display: flex; display: flex;
align-items: center; align-items: center;
gap: var(--sk-gap-m); gap: var(--sk-gap-m);
border-radius: var(--sk-br-m);
border: none; border: none;
cursor: pointer; cursor: pointer;
font-size: 1rem; font-size: 1rem;
@ -18,6 +16,7 @@
opacity: 0.9; opacity: 0.9;
} }
// Appearance
&[data-appearance="default"], &[data-appearance="primary"] { &[data-appearance="default"], &[data-appearance="primary"] {
background-color: var(--sk-primary-accent); background-color: var(--sk-primary-accent);
color: #fff; color: #fff;
@ -38,13 +37,33 @@
color: #fff; color: #fff;
background-color: transparent; background-color: transparent;
} }
// Size
&[data-size="small"] {
padding: var(--sk-gap-s) var(--sk-gap-l);
border-radius: var(--sk-br-m);
}
&[data-size="default"] {
padding: var(--sk-gap-m) var(--sk-gap-xl);
border-radius: var(--sk-br-m);
}
&[data-size="medium"] {
padding: var(--sk-gap-l) var(--sk-gap-xl);
border-radius: var(--sk-br-l);
gap: var(--sk-gap-l);
}
&[data-size="large"] {
padding: var(--sk-gap-xl) var(--sk-gap-xxxl);
border-radius: var(--sk-br-xl);
gap: var(--sk-gap-xl);
}
} }
} }
:host::ng-deep { :host::ng-deep {
button { button {
&[data-appearance="default"], &[data-appearance="primary"] { &[data-appearance="default"], &[data-appearance="primary"], &[data-appearance="secondary"] {
skirda-icon ng-icon { skirda-icon ng-icon {
color: #fff!important; color: #fff!important;

View File

@ -1,4 +1,6 @@
import { Component, Input, OnInit } from '@angular/core'; import { Component, ContentChild, Input } from '@angular/core';
import { IconComponent } from '../icon/icon.component';
import { ButtonAppearance, ButtonSize } from './button.interface'
@Component({ @Component({
selector: 'skirda-button', selector: 'skirda-button',
@ -6,7 +8,10 @@ import { Component, Input, OnInit } from '@angular/core';
styleUrls: ['./button.component.scss'], styleUrls: ['./button.component.scss'],
}) })
export class ButtonComponent { export class ButtonComponent {
@Input() appearance: string = 'default'; @ContentChild(IconComponent) icon!: IconComponent
@Input() appearance: ButtonAppearance = 'default';
@Input() size: ButtonSize = 'default'
constructor() {} constructor() {}
} }

View File

@ -1,6 +1,2 @@
export type ButtonAppearance = export type ButtonAppearance = 'default' | 'primary' | 'secondary' | 'outline' | 'flat';
| 'default' export type ButtonSize = 'default' | 'small' | 'medium' | 'large';
| 'primary'
| 'secondary'
| 'outline'
| 'flat';

View File

@ -1,12 +1,12 @@
<a [routerLink]="['/game/' + game.gameId]" routerLinkActive="active"> <a [routerLink]="['/game/' + game.gameId]" routerLinkActive="active">
<div class="sk-game"> <div class="sk-game" [ngClass]="{collapsed}">
<skirda-image-icon [src]="game.image"></skirda-image-icon> <skirda-image-icon [src]="game.image"></skirda-image-icon>
<div class="sk-game-info"> <div class="sk-game-info" *ngIf="!collapsed">
<div class="info-game-line"> <div class="info-game-line">
<skirda-heading size="6">{{game.title}}</skirda-heading> <skirda-heading size="6">{{game.title}}</skirda-heading>
</div> </div>
<skirda-text>description</skirda-text> <skirda-text>description</skirda-text>
</div> </div>
<skirda-icon name="play" size="24" (click)="run(game)" color="var(--sk-primary-accent)"></skirda-icon> <skirda-icon *ngIf="!collapsed" name="play" size="24" (click)="run(game)" color="var(--sk-primary-accent)"></skirda-icon>
</div> </div>
</a> </a>

View File

@ -32,11 +32,15 @@
opacity: 0.5; opacity: 0.5;
cursor: pointer; cursor: pointer;
&.collapsed {
border-radius: 2rem;
}
.sk-game-info { .sk-game-info {
flex: 1; flex: 1;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: var(--sk-gap-m); gap: var(--sk-gap-s);
} }
} }
} }

View File

@ -9,6 +9,7 @@ import { LauncherService } from 'src/app/services/launcher.service';
}) })
export class GameComponent implements OnInit { export class GameComponent implements OnInit {
@Input() game!: Game @Input() game!: Game
@Input() collapsed = false
constructor(private launcher: LauncherService) { } constructor(private launcher: LauncherService) { }

View File

@ -58,6 +58,7 @@
justify-content: center; justify-content: center;
align-items: center; align-items: center;
opacity: 0; opacity: 0;
z-index: 100;
&.shown { &.shown {
animation: showOverlay 0.2s ease 0s 1 forwards; animation: showOverlay 0.2s ease 0s 1 forwards;

View File

@ -1,7 +1,7 @@
<a [routerLink]="['/session/' + session.gameId]" routerLinkActive="active" [title]="session.status ?? 'No status'"> <a [routerLink]="['/session/' + session.gameId]" routerLinkActive="active" [title]="session.status ?? 'No status'">
<div class="sk-session"> <div class="sk-session" [ngClass]="{collapsed}">
<skirda-image-icon [src]="session.icon"></skirda-image-icon> <skirda-image-icon [src]="session.icon"></skirda-image-icon>
<div class="sk-session-info"> <div class="sk-session-info" *ngIf="!collapsed">
<div class="info-heading-line"> <div class="info-heading-line">
<skirda-heading size="6">{{session.title}}</skirda-heading> <skirda-heading size="6">{{session.title}}</skirda-heading>
</div> </div>
@ -10,6 +10,6 @@
<skirda-tag>{{session.expires | countdown | async}}</skirda-tag> <skirda-tag>{{session.expires | countdown | async}}</skirda-tag>
</div> </div>
</div> </div>
<skirda-icon name="play" size="24" (click)="run(session)" color="var(--sk-primary-accent)"></skirda-icon> <skirda-icon *ngIf="!collapsed" name="play" size="24" (click)="run(session)" color="var(--sk-primary-accent)"></skirda-icon>
</div> </div>
</a> </a>

View File

@ -33,11 +33,15 @@
opacity: 0.5; opacity: 0.5;
cursor: pointer; cursor: pointer;
&.collapsed {
border-radius: 2rem;
}
.sk-session-info { .sk-session-info {
flex: 1; flex: 1;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: var(--sk-gap-m); gap: var(--sk-gap-s);
.info-heading-line { .info-heading-line {
display: flex; display: flex;

View File

@ -10,6 +10,7 @@ import { LauncherService } from 'src/app/services/launcher.service'
}) })
export class SessionComponent implements OnInit { export class SessionComponent implements OnInit {
@Input() session!: Session; @Input() session!: Session;
@Input() collapsed = false
constructor( constructor(
private launcher: LauncherService private launcher: LauncherService

View File

@ -8,7 +8,7 @@
.sk-tag { .sk-tag {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
padding: 2px var(--sk-gap-m); padding: 0px var(--sk-gap-m);
border-radius: var(--sk-br-s); border-radius: var(--sk-br-s);
background-color: rgba(255, 255, 255, 0.05); background-color: rgba(255, 255, 255, 0.05);
} }

View File

@ -145,4 +145,5 @@
--sk-br-s: 0.25rem; --sk-br-s: 0.25rem;
--sk-br-m: 0.5rem; --sk-br-m: 0.5rem;
--sk-br-l: 0.75rem; --sk-br-l: 0.75rem;
--sk-br-xl: 1rem;
} }

View File

@ -35,7 +35,6 @@
width: 140px; width: 140px;
height: 140px; height: 140px;
overflow: hidden; overflow: hidden;
background-color: var(--sk-item-active);
mask-image: url('/assets/icon_mask.svg'); mask-image: url('/assets/icon_mask.svg');
mask-size: cover; mask-size: cover;
user-select: none; user-select: none;

View File

@ -1,9 +1,9 @@
<menu> <menu>
<div class="menu-search-panel"> <div class="menu-search-panel">
<skirda-input [formControl]="search" size="l" placeholder="Quick search"> <skirda-input *ngIf="!collapsed" [formControl]="search" size="l" placeholder="Quick search">
<skirda-icon name="search" size="18" role="left"></skirda-icon> <skirda-icon name="search" size="18" role="left"></skirda-icon>
</skirda-input> </skirda-input>
<button> <button (click)="toggle()">
<skirda-icon name="menu-panel" color="var(--sk-primary-accent)"></skirda-icon> <skirda-icon name="menu-panel" color="var(--sk-primary-accent)"></skirda-icon>
</button> </button>
</div> </div>
@ -26,8 +26,8 @@
<skirda-heading size="6">Downloads</skirda-heading> <skirda-heading size="6">Downloads</skirda-heading>
</skirda-section> </skirda-section>
</div> --> </div> -->
<app-menu-games></app-menu-games> <app-menu-games [collapsed]="collapsed"></app-menu-games>
<app-menu-sessions></app-menu-sessions> <app-menu-sessions [collapsed]="collapsed"></app-menu-sessions>
</div> </div>
<app-menu-profile></app-menu-profile> <app-menu-profile [collapsed]="collapsed"></app-menu-profile>
</menu> </menu>

View File

@ -2,6 +2,20 @@
display: block; display: block;
width: 340px; width: 340px;
&.collapsed {
width: 75px;
menu {
.menu-search-panel {
button {
flex: 1;
}
}
}
}
menu { menu {
display: flex; display: flex;
margin: 0; margin: 0;
@ -35,7 +49,6 @@
appearance: none; appearance: none;
background: none; background: none;
border: 0; border: 0;
transition: 0.1s ease;
border-radius: var(--sk-br-m); border-radius: var(--sk-br-m);
cursor: pointer; cursor: pointer;
flex-shrink: 0; flex-shrink: 0;

View File

@ -1,5 +1,6 @@
import { Component, OnInit } from '@angular/core'; import { Component, HostBinding, OnInit } from '@angular/core';
import { FormControl } from '@angular/forms'; import { FormControl } from '@angular/forms';
import { LocalStorage } from 'ngx-webstorage'
@Component({ @Component({
selector: 'app-main-menu', selector: 'app-main-menu',
@ -9,7 +10,15 @@ import { FormControl } from '@angular/forms';
export class MainMenuComponent implements OnInit { export class MainMenuComponent implements OnInit {
search = new FormControl<string>(''); search = new FormControl<string>('');
@HostBinding('class.collapsed')
@LocalStorage('menu-collapsed', false)
collapsed!: boolean
constructor() {} constructor() {}
ngOnInit(): void {} ngOnInit(): void {}
toggle () {
this.collapsed = !this.collapsed
}
} }

View File

@ -1,9 +1,11 @@
<skirda-section-label> <skirda-section-label *ngIf="!collapsed">
Pinned games <div class="count">{{games.length}}</div> Pinned games <div class="count">{{games.length}}</div>
</skirda-section-label> </skirda-section-label>
<skirda-icon name="library" size="20" *ngIf="collapsed"></skirda-icon>
<div class="games"> <div class="games">
<skirda-game <skirda-game
*ngFor="let game of games; index as i" *ngFor="let game of games; index as i"
[game]="game" [game]="game"
[collapsed]="collapsed"
></skirda-game> ></skirda-game>
</div> </div>

View File

@ -1,6 +1,11 @@
:host { :host {
display: block; display: block;
skirda-icon {
margin-bottom: 12px;
opacity: 0.5;
}
.games { .games {
display: flex; display: flex;
flex-direction: column; flex-direction: column;

View File

@ -1,4 +1,4 @@
import { Component, OnInit } from '@angular/core'; import { Component, Input, OnInit } from '@angular/core';
import { Game } from 'src/app/interfaces/game.interface' import { Game } from 'src/app/interfaces/game.interface'
import { GoGetGames } from 'src/app/services/go'; import { GoGetGames } from 'src/app/services/go';
@ -24,6 +24,8 @@ export class MenuGamesComponent implements OnInit {
games: Game[] = [] games: Game[] = []
@Input() collapsed: boolean = false
constructor() { } constructor() { }
ngOnInit(): void { ngOnInit(): void {

View File

@ -1,13 +1,13 @@
<a [routerLink]="['profile']" routerLinkActive="active" class="profile-area"> <a [routerLink]="['profile']" routerLinkActive="active" class="profile-area">
<skirda-avatar [username]="profile.username" size="small"></skirda-avatar> <skirda-avatar [username]="profile.username" size="default"></skirda-avatar>
<div class="profile-info"> <div class="profile-info" *ngIf="!collapsed">
<skirda-heading size="6">{{profile.username}}</skirda-heading> <skirda-heading size="6">{{profile.username}}</skirda-heading>
<skirda-text>online</skirda-text> <skirda-text>online</skirda-text>
</div> </div>
<a [routerLink]="['settings']"> <a [routerLink]="['settings']" *ngIf="!collapsed">
<skirda-icon name="settings" color="var(--sk-primary-accent)"></skirda-icon> <skirda-icon name="settings" color="var(--sk-primary-accent)"></skirda-icon>
</a> </a>
</a> </a>
<div class="theme-toggle" (click)="toggleTheme()" [ngClass]="{'active': theme === 'light'}"> <div class="theme-toggle" *ngIf="!collapsed" (click)="toggleTheme()" [ngClass]="{'active': theme === 'light'}">
<skirda-icon name="sun" color="var(--sk-primary-accent)"></skirda-icon> <skirda-icon name="sun" color="var(--sk-primary-accent)"></skirda-icon>
</div> </div>

View File

@ -1,4 +1,4 @@
import { Component, OnInit } from '@angular/core'; import { Component, Input, OnInit } from '@angular/core';
import { LocalStorage } from 'ngx-webstorage' import { LocalStorage } from 'ngx-webstorage'
import { Profile } from 'src/app/interfaces/profile.interface' import { Profile } from 'src/app/interfaces/profile.interface'
@ -14,6 +14,8 @@ export class MenuProfileComponent implements OnInit {
@LocalStorage('theme', 'dark') @LocalStorage('theme', 'dark')
public theme!: string public theme!: string
@Input() collapsed = false
constructor() { } constructor() { }
ngOnInit(): void { ngOnInit(): void {

View File

@ -1,9 +1,11 @@
<skirda-section-label> <skirda-section-label *ngIf="!collapsed">
Active sessions <div class="count">{{sessions.length}}</div> Active sessions <div class="count">{{sessions.length}}</div>
</skirda-section-label> </skirda-section-label>
<skirda-icon name="server" size="20" *ngIf="collapsed"></skirda-icon>
<div class="sessions"> <div class="sessions">
<skirda-session <skirda-session
*ngFor="let session of sessions; index as i" *ngFor="let session of sessions; index as i"
[session]="session" [session]="session"
[collapsed]="collapsed"
></skirda-session> ></skirda-session>
</div> </div>

View File

@ -1,6 +1,11 @@
:host { :host {
display: block; display: block;
skirda-icon {
margin-bottom: 12px;
opacity: 0.5;
}
.sessions { .sessions {
display: flex; display: flex;
flex-direction: column; flex-direction: column;

View File

@ -1,4 +1,4 @@
import { Component, OnInit } from '@angular/core'; import { Component, Input, OnInit } from '@angular/core';
import { Session } from 'src/app/interfaces/session.interface'; import { Session } from 'src/app/interfaces/session.interface';
import { GoGetSessions } from 'src/app/services/go'; import { GoGetSessions } from 'src/app/services/go';
@ -33,6 +33,7 @@ export class MenuSessionsComponent implements OnInit {
}, },
]; ];
sessions: Session[] = []; sessions: Session[] = [];
@Input() collapsed = false
constructor() {} constructor() {}

View File

@ -1,12 +1,15 @@
<!-- <ng-template [skirdaPopup]></ng-template>
<!-- File: name.component.html -->
<!-- 1. Создать ng-template с директивой [skirdaPopup], в котором
будет спавниться оверлей с окном (в будущем этот портал будет глобальным) -->
<ng-template [skirdaPopup]></ng-template>
<!-- 2. Если в окне нужно что-то более сложное, чем просто текст,
то нужно создать шаблон тела окна (то, что между шапкой окна и кнопками действий) -->
<ng-template #popupContent> <ng-template #popupContent>
<h1 style="font-size: 72px">😳🕶🤏</h1> <h1 style="font-size: 72px">😳🕶🤏</h1>
<p>Church-key listicle whatever hoodie hexagon pour-over ascot paleo tacos, organic aesthetic tbh meggings raclette.</p> <p>Church-key listicle whatever hoodie hexagon pour-over ascot paleo tacos, organic aesthetic tbh meggings raclette.</p>
</ng-template> --> </ng-template>
<!-- <skirda-button (click)="openPopup(popupContent)">
<skirda-icon name="play" size="16"></skirda-icon>
Run game
</skirda-button> -->
<div class="section"> <div class="section">
<div class="section-head"> <div class="section-head">
@ -41,7 +44,13 @@
<skirda-avatar *ngFor="let friend of friends" [username]="friend"></skirda-avatar> <skirda-avatar *ngFor="let friend of friends" [username]="friend"></skirda-avatar>
</div> </div>
</div> </div>
<!-- 6. Вызываем в нужном месте (в html компонента) открытие попап окна и передаем в метод из ts шаблон
(название переменной popupContent должно совпадать с аттрибутом
у описанного шаблона из пункта 2 (<ng-template #popupContent>)) -->
<skirda-button size="large" appearance="primary" (click)="openPopup(popupContent)">
<skirda-icon name="play" size="32"></skirda-icon>
<skirda-heading size="1">Play</skirda-heading> <skirda-heading size="1">Play</skirda-heading>
</skirda-button>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,12 +1,10 @@
import { inject, OnDestroy, TemplateRef, ViewChild } from '@angular/core'; import { Component, inject, OnDestroy, OnInit, TemplateRef, ViewChild } from '@angular/core'
import { Component, OnInit } from '@angular/core'; import { NavigationEnd, Router } from '@angular/router'
import { NavigationEnd, Router } from '@angular/router'; import { PopupDirective } from 'projects/ui/src/lib/popup/popup.directive'
import { PopupConfiguration } from 'projects/ui/src/lib/popup/popup.class'; import { PopupService } from 'projects/ui/src/lib/popup/popup.service'
import { PopupDirective } from 'projects/ui/src/lib/popup/popup.directive'; import { Subscription } from 'rxjs'
import { PopupService } from 'projects/ui/src/lib/popup/popup.service'; import { Game } from 'src/app/interfaces/game.interface'
import { first, Subscription } from 'rxjs'; import { UiService } from 'src/app/services/ui.service'
import { Game } from 'src/app/interfaces/game.interface';
import { UiService } from 'src/app/services/ui.service';
@Component({ @Component({
selector: 'app-game-page', selector: 'app-game-page',
@ -14,7 +12,10 @@ import { UiService } from 'src/app/services/ui.service';
styleUrls: ['./game-page.component.scss'], styleUrls: ['./game-page.component.scss'],
}) })
export class GamePageComponent implements OnInit, OnDestroy { export class GamePageComponent implements OnInit, OnDestroy {
// @ViewChild(PopupDirective, { static: true }) popupPortal!: PopupDirective; // File: name.component.ts
// 3. Получить в классе компонента элемент с директивой (портал из 1 пункта)
@ViewChild(PopupDirective, { static: true }) popupPortal!: PopupDirective;
uiService = inject(UiService) uiService = inject(UiService)
subs: Map<string, Subscription> = new Map(); subs: Map<string, Subscription> = new Map();
game: Game = { game: Game = {
@ -40,8 +41,11 @@ export class GamePageComponent implements OnInit, OnDestroy {
] ]
constructor( constructor(
private router: Router private router: Router,
) {} // private popup: PopupService
// 4. Импортировать сервис для попап окон
private popup: PopupService
) {}
ngOnInit(): void { ngOnInit(): void {
this.observeNavigation() this.observeNavigation()
@ -64,39 +68,24 @@ export class GamePageComponent implements OnInit, OnDestroy {
return this.tempImages[Math.floor(0 + Math.random() * ((this.tempImages.length - 1) + 1 - 0))] return this.tempImages[Math.floor(0 + Math.random() * ((this.tempImages.length - 1) + 1 - 0))]
} }
// openPopup(template?: TemplateRef<any>) { // 5. Написать метод для вызова попап окна, куда аргументом передается
// this.subs.set( // шаблон тела окна (из пункта 2)
// 'popup-events', openPopup(template?: TemplateRef<any>) {
// this.popup // Здесь мы подписываемся на ивента попап окна и сохраняем подписку, чтобы потом
// .open(this.popupPortal, undefined, { // при дестрое компонента можно было от них отписаться
// header: { closable: false, title: 'Warning' }, this.subs.set(
// actions: [ 'popup-events',
// { // при вызове передается:
// title: 'I understand, delete file', // 1. popupPortal - портал для спавна оверлея с окном (пункт 1 в html, пункт 3 в ts),
// event: 'delete', // 2. template - шаблон тела окна, если он есть
// appearance: 'primary', // 3. объект с настройками окна (необязательный параметр), где можно кастомизировать внешний вид
// }, // типа PopupConfig (\projects\ui\src\lib\popup\popup.class.ts)
// { this.popup
// title: 'Do not delete', .open(this.popupPortal, template, {})
// event: 'cancel', .subscribe((event) => {
// appearance: 'outline', // здесь описываются обработчики по нажатию на кнопки действий в попап окне
// }, console.log(`event: ${event}`);
// // { })
// // title: 'Primary button', );
// // event: 'button-2', }
// // appearance: 'primary',
// // },
// ],
// overlay: {
// closable: true,
// // background: 'rgba(255, 255, 255, 1)',
// },
// body: 'Humblebrag chillwave sriracha, ramps polaroid distillery edison bulb palo santo etsy sus pabst knausgaard typewriter dreamcatcher.',
// theme: 'dark',
// })
// .subscribe((event) => {
// console.log(`event: ${event}`);
// })
// );
// }
} }