mini-profile, dark/light themes, fixes

This commit is contained in:
svensken 2022-11-04 03:45:08 +03:00
parent c068b91a73
commit 55fa46b4d4
51 changed files with 710 additions and 230 deletions

View File

@ -29,7 +29,10 @@
"src/assets"
],
"styles": [
"src/styles.scss"
"src/styles.scss",
"src/_themes.scss",
"src/_light.scss",
"src/_dark.scss"
],
"scripts": []
},
@ -97,7 +100,10 @@
"src/assets"
],
"styles": [
"src/styles.scss"
"src/styles.scss",
"src/_themes.scss",
"src/_light.scss",
"src/_dark.scss"
],
"scripts": []
}

51
package-lock.json generated
View File

@ -19,8 +19,8 @@
"@angular/router": "^14.1.0",
"@ng-icons/core": "^22.4.0",
"@ng-icons/tabler-icons": "^22.4.0",
"fast-average-color": "^9.1.1",
"moment": "^2.29.4",
"ngx-webstorage": "^10.0.1",
"rxjs": "~7.5.0",
"tslib": "^2.3.0",
"zone.js": "~0.11.4"
@ -3103,11 +3103,6 @@
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.0.tgz",
"integrity": "sha512-IOXCvVRToe7e0ny7HpT/X9Rb2RYtElG1a+VshjwT00HxrM2dWBApHQoqsI6WiY7Q03vdf2bCrIGzVrkF/5t10w=="
},
"node_modules/@types/offscreencanvas": {
"version": "2019.7.0",
"resolved": "https://registry.npmjs.org/@types/offscreencanvas/-/offscreencanvas-2019.7.0.tgz",
"integrity": "sha512-PGcyveRIpL1XIqK8eBsmRBt76eFgtzuPiSTyKHZxnGemp2yzGzWpjYKAfK3wIMiU7eH+851yEpiuP8JZerTmWg=="
},
"node_modules/@types/parse-json": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
@ -5664,17 +5659,6 @@
"node": ">=4"
}
},
"node_modules/fast-average-color": {
"version": "9.1.1",
"resolved": "https://registry.npmjs.org/fast-average-color/-/fast-average-color-9.1.1.tgz",
"integrity": "sha512-PJizLBcGb/jqUzrH66385te4+GcOK7wcUiCDvBUszdpzc/pvV1kwifvvsFygV3mS+7qwnWmK9/BrZniaOOC9ag==",
"dependencies": {
"@types/offscreencanvas": "^2019.7.0"
},
"engines": {
"node": ">= 12"
}
},
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@ -7943,6 +7927,18 @@
"node": "^12.20.0 || >=14"
}
},
"node_modules/ngx-webstorage": {
"version": "10.0.1",
"resolved": "https://registry.npmjs.org/ngx-webstorage/-/ngx-webstorage-10.0.1.tgz",
"integrity": "sha512-OWmzAzby+/UrbRY/5d229Y4NzFn1a/u2WSEeZqzY5lwB/3d8ODZ6mlW/BZGIuuZ48Hp8tXMM3pFCz9+pEyzvDA==",
"dependencies": {
"tslib": "^2.0.0"
},
"peerDependencies": {
"@angular/common": "^14.0.0",
"@angular/core": "^14.0.0"
}
},
"node_modules/nice-napi": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz",
@ -13868,11 +13864,6 @@
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.0.tgz",
"integrity": "sha512-IOXCvVRToe7e0ny7HpT/X9Rb2RYtElG1a+VshjwT00HxrM2dWBApHQoqsI6WiY7Q03vdf2bCrIGzVrkF/5t10w=="
},
"@types/offscreencanvas": {
"version": "2019.7.0",
"resolved": "https://registry.npmjs.org/@types/offscreencanvas/-/offscreencanvas-2019.7.0.tgz",
"integrity": "sha512-PGcyveRIpL1XIqK8eBsmRBt76eFgtzuPiSTyKHZxnGemp2yzGzWpjYKAfK3wIMiU7eH+851yEpiuP8JZerTmWg=="
},
"@types/parse-json": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
@ -15710,14 +15701,6 @@
"tmp": "^0.0.33"
}
},
"fast-average-color": {
"version": "9.1.1",
"resolved": "https://registry.npmjs.org/fast-average-color/-/fast-average-color-9.1.1.tgz",
"integrity": "sha512-PJizLBcGb/jqUzrH66385te4+GcOK7wcUiCDvBUszdpzc/pvV1kwifvvsFygV3mS+7qwnWmK9/BrZniaOOC9ag==",
"requires": {
"@types/offscreencanvas": "^2019.7.0"
}
},
"fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@ -17383,6 +17366,14 @@
}
}
},
"ngx-webstorage": {
"version": "10.0.1",
"resolved": "https://registry.npmjs.org/ngx-webstorage/-/ngx-webstorage-10.0.1.tgz",
"integrity": "sha512-OWmzAzby+/UrbRY/5d229Y4NzFn1a/u2WSEeZqzY5lwB/3d8ODZ6mlW/BZGIuuZ48Hp8tXMM3pFCz9+pEyzvDA==",
"requires": {
"tslib": "^2.0.0"
}
},
"nice-napi": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz",

View File

@ -21,8 +21,8 @@
"@angular/router": "^14.1.0",
"@ng-icons/core": "^22.4.0",
"@ng-icons/tabler-icons": "^22.4.0",
"fast-average-color": "^9.1.1",
"moment": "^2.29.4",
"ngx-webstorage": "^10.0.1",
"rxjs": "~7.5.0",
"tslib": "^2.3.0",
"zone.js": "~0.11.4"

View File

@ -19,19 +19,19 @@
}
&[data-appearance="default"], &[data-appearance="primary"] {
background-color: var(--sk-primary);
background-color: var(--sk-primary-accent);
color: #fff;
}
&[data-appearance="secondary"] {
background-color: var(--sk-secondary);
background-color: var(--sk-secondary-accent);
color: #fff;
}
&[data-appearance="outline"] {
color: #fff;
background-color: transparent;
box-shadow: 0px 0px 0px 1px rgba(255, 255, 255, 0.1);
box-shadow: 0px 0px 0px 1px var(--sk-border);
}
&[data-appearance="flat"] {
@ -40,3 +40,15 @@
}
}
}
:host::ng-deep {
button {
&[data-appearance="default"], &[data-appearance="primary"] {
skirda-icon ng-icon {
color: #fff!important;
}
}
}
}

View File

@ -1,18 +1,21 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { KeyboardDirective } from './keyboard.directive';
import { KeyboardDirective } from './keyboard/keyboard.directive';
import { IsScrollingDirective } from './is-scrolling/is-scrolling.directive';
@NgModule({
declarations: [
KeyboardDirective
KeyboardDirective,
IsScrollingDirective
],
imports: [
CommonModule
],
exports: [
KeyboardDirective
KeyboardDirective,
IsScrollingDirective
]
})
export class DirectivesModule { }

View File

@ -0,0 +1,8 @@
import { IsScrollingDirective } from './is-scrolling.directive';
describe('IsScrollingDirective', () => {
it('should create an instance', () => {
const directive = new IsScrollingDirective();
expect(directive).toBeTruthy();
});
});

View File

@ -0,0 +1,15 @@
import { Directive, ElementRef, HostBinding } from '@angular/core';
@Directive({
selector: '[skirdaIsScrolling]'
})
export class IsScrollingDirective {
constructor(
private element: ElementRef<HTMLElement>
) { }
@HostBinding('attr.data-scroll-y') get isScrollableY () {
return this.element.nativeElement.clientHeight < this.element.nativeElement.scrollHeight
}
}

View File

@ -1,3 +1,4 @@
<a [routerLink]="['/game/' + game.gameId]" routerLinkActive="active">
<div class="sk-game">
<skirda-image-icon [src]="game.image"></skirda-image-icon>
<div class="sk-game-info">
@ -6,5 +7,6 @@
</div>
<skirda-text>description</skirda-text>
</div>
<skirda-icon name="play" size="24" (click)="run(game)" color="var(--sk-primary)"></skirda-icon>
<skirda-icon name="play" size="24" (click)="run(game)" color="var(--sk-primary-accent)"></skirda-icon>
</div>
</a>

View File

@ -1,6 +1,27 @@
:host {
display: block;
a {
display: block;
color: inherit;
text-decoration: none;
&:hover {
.sk-game {
opacity: 0.5;
background-color: var(--sk-item-active);
}
}
&.active {
.sk-game {
opacity: 1;
background-color: var(--sk-item-active);
}
}
.sk-game {
display: flex;
align-items: center;
@ -11,21 +32,12 @@
opacity: 0.5;
cursor: pointer;
&:hover {
opacity: 1;
background-color: rgba(255, 255, 255, 0.05);
}
.sk-game-info {
flex: 1;
}
}
&.active {
.sk-game {
opacity: 1;
background-color: rgba(255, 255, 255, 0.1);
display: flex;
flex-direction: column;
gap: var(--sk-gap-m);
}
}
}
}

View File

@ -5,6 +5,7 @@ import { ImageIconModule } from '../image-icon/image-icon.module'
import { TypographyModule } from '../typography/typography.module'
import { TagModule } from '../tag/tag.module'
import { IconModule } from '../icon/icon.module'
import { RouterModule } from '@angular/router'
@NgModule({
declarations: [
@ -15,7 +16,8 @@ import { IconModule } from '../icon/icon.module'
ImageIconModule,
TypographyModule,
TagModule,
IconModule
IconModule,
RouterModule
],
exports: [
GameComponent

View File

@ -14,7 +14,7 @@ export class IconComponent {
@Input() name!: string;
@Input() size: number | string = 24;
@Input() sizeUnit: string = 'px';
@Input() color: string = '#fff';
@Input() color: string = 'var(--sk-text)';
get sizeWithUnit() {
return this.size + this.sizeUnit;

View File

@ -12,6 +12,8 @@ import {
tablerDownload,
tablerPlayerPlay,
tablerX,
tablerSettings,
tablerSun
} from '@ng-icons/tabler-icons';
const icons = {
@ -24,6 +26,8 @@ const icons = {
tablerDownload,
tablerPlayerPlay,
tablerX,
tablerSettings,
tablerSun
};
@NgModule({

View File

@ -8,5 +8,7 @@ icons.set('friends', 'tablerUsers');
icons.set('download', 'tablerDownload');
icons.set('play', 'tablerPlayerPlay');
icons.set('close', 'tablerX');
icons.set('settings', 'tablerSettings');
icons.set('sun', 'tablerSun');
export { icons };

View File

@ -7,12 +7,14 @@
gap: 0.5rem;
padding: var(--local-gap);
cursor: text;
background-color: var(--sk-input);
background-color: transparent;
border: 1px solid var(--sk-border);
box-sizing: border-box;
color: #fff;
border-radius: 8px;
&:focus-within {
background-color: var(--sk-input-focus);
border: 1px solid var(--sk-border-hovered);
}
input {
@ -21,11 +23,13 @@
outline: none;
appearance: none;
background: transparent;
font-size: 0.875rem;
color: #fff;
font-size: 1rem;
color: var(--sk-text);
font-family: var(--font);
&::placeholder {
color: rgba(255, 255, 255, 0.5);
color: var(--sk-text);
opacity: 0.5;
}
}
}

View File

@ -18,9 +18,13 @@ export class CountdownPipe implements PipeTransform {
let days = duration.days(), hours = duration.hours(), minutes = duration.minutes(), seconds = duration.seconds()
if (days > 0)
return `${days} ${days == 1 ? 'day' : 'days'}`
else {
if (seconds < 0)
return 'expired'
else
return `${this.pad(hours)}:${this.pad(minutes)}:${this.pad(seconds)}`
}
}
private pad (value: number) {
return value.toString().padStart(2, '0')

View File

@ -88,15 +88,15 @@
}
&[data-theme="dark"] {
--local-background: #222;
--local-color: #fff;
--local-border: rgba(255, 255, 255, 0.125);
--local-background: var(--sk-background);
--local-color: var(--sk-text);
--local-border: var(--sk-border);
background: linear-gradient(45deg, rgba(255, 255, 255, 0.1), rgba(255, 255, 255, 0.25));
}
&[data-theme="light"] {
--local-background: #fff;
--local-color: #111;
--local-border: rgba(0, 0, 0, 0.125);
--local-background: var(--sk-background);
--local-color: var(--sk-text);
--local-border: var(--sk-border);
background: linear-gradient(45deg, rgba(0, 0, 0, 0.15), rgba(0, 0, 0, 0.25));
}

View File

@ -14,8 +14,8 @@
border-radius: var(--sk-br-m);
&:hover {
opacity: 1;
background-color: rgba(255, 255, 255, 0.05);
opacity: 0.5;
background-color: var(--sk-item-active);
}
}
@ -23,7 +23,7 @@
.sk-section {
opacity: 1;
background-color: rgba(255, 255, 255, 0.1);
background-color: var(--sk-item-active);
}
}
}

View File

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

View File

@ -1,6 +1,28 @@
:host {
display: block;
a {
display: block;
color: inherit;
text-decoration: none;
&:hover {
.sk-session {
opacity: 0.5;
background-color: var(--sk-item-active);
}
}
&.active {
.sk-session {
opacity: 1;
background-color: var(--sk-item-active);
}
}
.sk-session {
display: flex;
align-items: center;
@ -11,28 +33,23 @@
opacity: 0.5;
cursor: pointer;
&:hover {
opacity: 1;
background-color: rgba(255, 255, 255, 0.05);
}
.sk-session-info {
flex: 1;
display: flex;
flex-direction: column;
gap: var(--sk-gap-s);
gap: var(--sk-gap-m);
.info-heading-line {
display: flex;
justify-content: space-between;
align-items: baseline;
gap: var(--sk-gap-m);
}
.info-line {
display: flex;
align-items: baseline;
gap: var(--sk-gap-m);
max-width: 248px;
max-width: 234px;
skirda-text {
flex: 1;
@ -44,12 +61,5 @@
}
}
}
&.active {
.sk-session {
opacity: 1;
background-color: rgba(255, 255, 255, 0.1);
}
}
}

View File

@ -1,5 +1,7 @@
import { Component, Input, OnInit } from '@angular/core';
import { Game } from 'src/app/interfaces/game.interface'
import { Session } from 'src/app/interfaces/session.interface';
import { LauncherService } from 'src/app/services/launcher.service'
@Component({
selector: 'skirda-session[session]',
@ -9,7 +11,15 @@ import { Session } from 'src/app/interfaces/session.interface';
export class SessionComponent implements OnInit {
@Input() session!: Session;
constructor() {}
constructor(
private launcher: LauncherService
) {}
ngOnInit(): void {}
run(session: Session) {
// TODO: run session
//console.log('Game to run ' + game.title)
// this.launcher.RunGame(game)
}
}

View File

@ -5,10 +5,12 @@ import { ImageIconModule } from '../image-icon/image-icon.module';
import { TypographyModule } from '../typography/typography.module';
import { TagModule } from '../tag/tag.module'
import { PipesModule } from '../pipes/pipes.module'
import { RouterModule } from '@angular/router'
import { IconModule } from '../icon/icon.module'
@NgModule({
declarations: [SessionComponent],
imports: [CommonModule, ImageIconModule, TypographyModule, TagModule, PipesModule],
imports: [CommonModule, ImageIconModule, TypographyModule, TagModule, PipesModule, RouterModule, IconModule],
exports: [SessionComponent],
})
export class SessionModule {}

View File

@ -1,13 +1,14 @@
:host {
&.filled .sk-tag {
background-color: var(--sk-primary);
background-color: var(--sk-primary-accent);
color: #fff;
}
.sk-tag {
display: inline-flex;
align-items: center;
padding: 1px var(--sk-gap-m);
padding: 2px var(--sk-gap-m);
border-radius: var(--sk-br-s);
background-color: rgba(255, 255, 255, 0.05);
}

View File

@ -4,22 +4,22 @@
font-weight: 600;
&[data-size="1"] {
font-size: 32px;
font-size: var(--sk-typo-h1);
}
&[data-size="2"] {
font-size: 28px;
font-size: var(--sk-typo-h2);
}
&[data-size="3"] {
font-size: 24px;
font-size: var(--sk-typo-h3);
}
&[data-size="4"] {
font-size: 22px;
font-size: var(--sk-typo-h4);
}
&[data-size="5"] {
font-size: 18px;
font-size: var(--sk-typo-h5);
}
&[data-size="6"] {
font-size: 15px;
font-size: var(--sk-typo-h6);
}
}
}

View File

@ -1,4 +1,4 @@
:host {
display: inline;
font-size: 0.875rem;
font-size: var(--sk-typo-base);
}

29
src/_dark.scss Normal file
View File

@ -0,0 +1,29 @@
@import 'src/themes';
// modifiers
$modifier-item-hover: -1%;
$modifier-gradient-lightness: +20%;
$ui-background: #191919;
$ui-text: #ffffff;
$ui-gray-text: #959595;
$ui-foreground: #252525;
$ui-left-menu: rgba(0,0,0,0.65);
$ui-right-content: rgba(0,0,0,0.45);
$ui-shadow: rgba(95, 95, 95, 0.15);
$ui-item-active: rgba(255, 255, 255, 0.1);
$ui-primary-accent: #2698e4;
$ui-secondary-accent: #9a74e0;
$ui-secondary-accent-text: #ffffff;
$ui-border: rgba(255, 255, 255, 0.1);
$ui-negative: #ff4f4f;
$ui-positive: #3ac52b;
$ui-warning: #fd8a09;
$ui-palette: #F8505D, #DC496C, #C8449C, #E362B7, #975BEB, #7A7AF3, #5C99FA, #37B3FA, #71CBAF, #7ACB71, #A5D458, #F7B433, #F89C35, #FB5607;
$ui-overlay: rgba(22, 22, 22, 0.4);
$ui-dark-overlay: rgba(0, 0, 0, 0.2);
$ui-black: #000000;
@mixin dark {
@include theme
}

29
src/_light.scss Normal file
View File

@ -0,0 +1,29 @@
@import 'src/themes';
//modifiers
$modifier-item-hover: +30%;
$modifier-gradient-lightness: +50%;
$ui-background: #ffffff;
$ui-text: #000000;
$ui-gray-text: #8c8c8c;
$ui-foreground: #f4f4f4;
$ui-left-menu: rgba(255, 255, 255, 0.75);
$ui-right-content: rgba(255,255,255,0.35);
$ui-shadow: rgba(50, 50, 50, 0.1);
$ui-item-active: rgba(0,0,0,0.075);
$ui-secondary-accent: #715AB6;
$ui-primary-accent: #5f74ff;
$ui-secondary-accent-text: #ffffff;
$ui-border: rgba(0,0,0,0.25);
$ui-negative: #ff3939;
$ui-positive: #3ac52b;
$ui-warning: #fd8a09;
$ui-palette: #FD2B3B,#DA224E,#C11C89,#AD177A,#8338EC,#5F5FF6,#3A86FF,#0BA7FF,#54C4A2,#5FC454,#95D035,#FBA807,#FD8A09,#FB5607;
$ui-overlay: rgba(55, 55, 55, 0.35);
$ui-dark-overlay: rgba(0, 0, 0, 0.7);
$ui-black: #000000;
@mixin light {
@include theme
}

144
src/_themes.scss Normal file
View File

@ -0,0 +1,144 @@
@use 'sass:color';
@mixin gradientY ($colors) {
background: linear-gradient(to bottom, $colors);
}
@function shade ($color, $opacity) {
@return transparentize($color, $opacity);
}
@function gradient ($color, $value: +50%) {
$top: scale-color($color, $lightness: $value);
$bottom: $color;
@return #{$top}, #{$bottom};
}
@function colorsGradient ($colors) {
$line: "";
@for $i from 1 through length($colors) {
@if ($i > 1) {
$line: $line + ',';
}
$line: $line + #{nth($colors, $i)}
}
@return $line;
}
@function hovered ($color, $value: +10%) {
@return scale-color($color, $lightness: $value);
}
@mixin theme {
// base
--sk-background : #{$ui-background};
--sk-background-75 : #{shade($ui-background, 0.25)};
--sk-background-50 : #{shade($ui-background, 0.5)};
--sk-text : #{$ui-text};
--sk-gray-text : #{$ui-gray-text};
--sk-foreground : #{$ui-foreground};
--sk-foreground-hovered : #{hovered($ui-foreground)};
--sk-left-menu : #{$ui-left-menu};
--sk-right-content : #{$ui-right-content};
--sk-item-active : #{$ui-item-active};
--sk-item-hovered : #{hovered($ui-item-active, $modifier-item-hover)};
--sk-shadow : #{shade($ui-shadow, 0)};
--sk-shadow-hovered : #{shade($ui-shadow, 0)};
// primary color
--sk-primary-accent : #{$ui-primary-accent};
--sk-primary-accent-hovered : #{hovered($ui-primary-accent)};
@for $i from 9 through 1 {
--sk-primary-accent-#{10 * $i}: #{shade($ui-primary-accent, 1 - 0.1 * $i)};
}
--sk-primary-accent-5: #{shade($ui-primary-accent, 0.95)};
--sk-primary-accent-gradient: #{gradient(shade($ui-primary-accent, 0), $modifier-gradient-lightness)};
// secondary color
--sk-secondary-accent : #{$ui-secondary-accent};
--sk-secondary-accent-hovered : #{hovered($ui-secondary-accent)};
@for $i from 9 through 1 {
--sk-secondary-accent-#{10 * $i}: #{shade($ui-secondary-accent, 1 - 0.1 * $i)};
}
--sk-secondary-accent-5: #{shade($ui-secondary-accent, 0.95)};
--sk-secondary-accent-text : #{$ui-secondary-accent-text};
// border
--sk-border : #{$ui-border};
--sk-border-hovered : #{hovered($ui-border)};
// negative
--sk-negative : #{$ui-negative};
@for $i from 9 through 1 {
--sk-negative-#{10 * $i}: #{shade($ui-negative, 1 - 0.1 * $i)};
}
--sk-negative-5: #{shade($ui-negative, 0.95)};
--sk-negative-hovered : #{hovered($ui-negative)};
// positive
--sk-positive : #{$ui-positive};
@for $i from 9 through 1 {
--sk-positive-#{10 * $i}: #{shade($ui-positive, 1 - 0.1 * $i)};
}
--sk-positive-5: #{shade($ui-positive, 0.95)};
--sk-positive-hovered : #{hovered($ui-positive)};
// warning
--sk-warning : #{$ui-warning};
@for $i from 9 through 1 {
--sk-warning-#{10 * $i}: #{shade($ui-warning, 1 - 0.1 * $i)};
}
--sk-warning-5: #{shade($ui-warning, 0.95)};
--sk-warning-hovered : #{hovered($ui-warning)};
// palette colors & gradients
@for $i from 1 through length($ui-palette) {
--sk-palette-#{$i - 1}-5: #{shade(nth($ui-palette, $i), 0.95)};
--sk-palette-#{$i - 1}: #{nth($ui-palette, $i)};
--sk-palette-gradient-#{$i - 1}: #{gradient(nth($ui-palette, $i), $modifier-gradient-lightness)};
@for $j from 9 through 1 {
--sk-palette-#{$i - 1}-#{10 * $j}: #{shade(nth($ui-palette, $i), 1 - 0.1 * $j)};
--sk-palette-gradient-#{$i - 1}-#{10 * $j}: #{gradient(shade(nth($ui-palette, $i), 1 - 0.1 * $j), $modifier-gradient-lightness)};
}
--sk-palette-#{$i - 1}-5: #{shade(nth($ui-palette, $i), 0.95)};
--sk-palette-gradient-#{$i - 1}-5: #{gradient(shade(nth($ui-palette, $i), 0.95), $modifier-gradient-lightness)};
}
--sk-overlay: #{$ui-overlay};
--sk-overlay-50: #{shade($ui-overlay, 0.2)};
--sk-dark-overlay: #{$ui-dark-overlay};
// black & shades
--sk-black: #{$ui-black};
@for $i from 9 through 1 {
--sk-black-#{10 * $i}: #{shade($ui-black, 1 - 0.1 * $i)};
}
--sk-typo-base: 14px;
--sk-typo-special: 13px;
--sk-typo-title: 24px;
--sk-typo-subtitle: 12px;
--sk-typo-h1: 26px;
--sk-typo-h2: 24px;
--sk-typo-h3: 22px;
--sk-typo-h4: 20px;
--sk-typo-h5: 17px;
--sk-typo-h6: 15px;
// gaps
--sk-gap-xs: 0.125rem;
--sk-gap-s: 0.25rem;
--sk-gap-m: 0.5rem;
--sk-gap-l: 0.75rem;
--sk-gap-xl: 1rem;
// border-radius
--sk-br-s: 0.25rem;
--sk-br-m: 0.5rem;
}

View File

@ -1,5 +1,6 @@
import { Component, OnInit, AfterViewInit } from '@angular/core';
import { Router, RouterEvent } from '@angular/router';
import { LocalStorage, LocalStorageService } from 'ngx-webstorage'
import { GoWebViewInit } from './services/go';
@Component({
@ -9,16 +10,30 @@ import { GoWebViewInit } from './services/go';
})
export class AppComponent implements OnInit{
constructor(private router: Router){ }
title = 'go-web';
constructor(private router: Router, private storage: LocalStorageService) { }
@LocalStorage('theme', 'dark')
public theme!: string
ngOnInit(){
document.addEventListener('GoChangeRoute_Callback', (event:any) => {
this.router.navigate([event.detail])
console.log("Navigating to route:" + event.detail)
})
this.setTheme(this.theme)
this.storage.observe('theme').subscribe(value => {
this.setTheme(this.theme)
})
}
ngAfterViewInit(){
GoWebViewInit()
}
title = 'go-web';
setTheme (theme: string) {
document.querySelector('html')?.setAttribute('data-theme', theme)
}
}

View File

@ -16,6 +16,7 @@ import { SandboxModule } from './components/sandbox/sandbox.module';
import { SignInModule } from './components/sign-in/sign-in.module';
import { LoadingScreenComponent } from './components/loading-screen/loading-screen.component';
import { UiModule } from 'projects/ui/src/lib/ui.module'
import { NgxWebstorageModule } from 'ngx-webstorage'
@NgModule({
declarations: [AppComponent, LoadingScreenComponent],
@ -28,7 +29,8 @@ import { UiModule } from 'projects/ui/src/lib/ui.module'
PlaygroundModule,
SandboxModule,
SignInModule,
UiModule
UiModule,
NgxWebstorageModule.forRoot({ prefix: 'skirda', separator: '.', caseSensitive: true })
],
providers: [
{ provide: APP_BASE_HREF, useValue: '/' },

View File

@ -20,7 +20,6 @@
from {
opacity: 1;
scale: 0;
background-color: #d6282800;
}
50% {
opacity: 0.5;
@ -28,7 +27,6 @@
to {
opacity: 0;
scale: 3;
background-color: #d6282899;
}
}
@ -37,7 +35,7 @@
width: 140px;
height: 140px;
overflow: hidden;
background-color: rgba(255, 255, 255, 0.1);
background-color: var(--sk-item-active);
mask-image: url('/assets/icon_mask.svg');
mask-size: cover;
user-select: none;
@ -58,17 +56,15 @@
translate: -50% -50%;
width: 24px;
height: 24px;
border: 1.5px solid var(--sk-primary);
border: 1.5px solid var(--sk-primary-accent);
border-radius: 50%;
animation: loaderPulse 1.4s linear -0.7s infinite forwards;
mask-image: linear-gradient(to bottom, black 0%, black 40%, transparent 75%);
z-index: 0;
box-shadow: inset 0px 0.25px 0px 0px rgba(255, 255, 255, 0.5);
}
&:after {
animation-delay: 0s;
border: 1.5px solid var(--sk-secondary);
}
skirda-heading {

View File

@ -1,13 +1,14 @@
<menu>
<div class="sk-search-panel">
<div class="menu-search-panel">
<skirda-input [formControl]="search" size="l" placeholder="Quick search">
<skirda-icon name="search" size="18" role="left"></skirda-icon>
</skirda-input>
<button>
<skirda-icon name="menu-panel" color="var(--sk-primary)"></skirda-icon>
<skirda-icon name="menu-panel" color="var(--sk-primary-accent)"></skirda-icon>
</button>
</div>
<div class="sk-sections">
<div class="menu-content" skirdaIsScrolling>
<div class="menu-sections">
<!-- Здесь class="active" для подсвечивания активного раздела прописывается автоматически,
если URL после перехода соддержит routerLink (класс прописывается в routerLinkActive) -->
<skirda-section [routerLink]="['servers']" routerLinkActive="active">
@ -29,4 +30,6 @@
</div>
<app-menu-games></app-menu-games>
<app-menu-sessions></app-menu-sessions>
</div>
<app-menu-profile></app-menu-profile>
</menu>

View File

@ -9,24 +9,24 @@
flex-direction: column;
width: 100%;
height: 100%;
background-color: rgba(0,0,0,0.65);
padding: 2rem 0.5rem 0.5rem 0.5rem;
background-color: var(--sk-left-menu);
padding: 0.5rem 0.5rem 0.5rem 0.5rem;
box-sizing: border-box;
backdrop-filter: blur(64px);
overflow-y: auto;
gap: 1rem;
.sk-search-panel {
.menu-search-panel {
display: flex;
gap: 0.625rem;
flex-shrink: 0;
skirda-input {
flex: 1;
}
button {
height: 100%;
aspect-ratio: 1/1;
height: 42px;
width: 42px;
padding: 0;
margin: 0;
outline: none;
@ -37,17 +37,37 @@
border-radius: var(--sk-br-m);
cursor: pointer;
flex-shrink: 0;
color: var(--sk-primary-accent);
&:hover {
background-color: rgba(255, 255, 255, 0.05);
background-color: var(--sk-primary-accent-20);
}
}
}
.sk-sections {
.menu-content {
flex: 1;
overflow-y: auto;
padding: 1.25rem 0rem;
display: flex;
flex-direction: column;
gap: var(--sk-gap-l);
&[data-scroll-y="true"] {
mask-image: linear-gradient(to bottom, transparent 0%, black 1.25rem, black calc(100% - 1.25rem), transparent);
padding-right: 0.5rem;
}
.menu-sections {
display: grid;
grid-template-columns: 1fr 1fr;
flex-direction: column;
gap: 0.1875rem;
}
}
app-menu-profile {
flex-shrink: 0;
}
}
}

View File

@ -3,7 +3,6 @@
</skirda-section-label>
<div class="games">
<skirda-game
[routerLink]="['/game/' + game.gameId]" routerLinkActive="active"
*ngFor="let game of games; index as i"
[game]="game"
></skirda-game>

View File

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

View File

@ -0,0 +1,36 @@
:host {
display: flex;
gap: var(--sk-gap-l);
padding: var(--sk-gap-l);
align-items: center;
.profile-info {
flex: 1;
display: flex;
flex-direction: column;
gap: var(--sk-gap-s);
skirda-text {
opacity: 0.5;
}
}
skirda-icon {
cursor: pointer;
}
}
:host::ng-deep {
.theme-toggle {
padding: var(--sk-gap-s);
border-radius: 2rem;
&.active {
background-color: var(--sk-primary-accent);
skirda-icon ng-icon {
color: #fff!important;
}
}
}
}

View File

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { MenuProfileComponent } from './menu-profile.component';
describe('MenuProfileComponent', () => {
let component: MenuProfileComponent;
let fixture: ComponentFixture<MenuProfileComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ MenuProfileComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(MenuProfileComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,25 @@
import { Component, OnInit } from '@angular/core';
import { LocalStorage } from 'ngx-webstorage'
import { Profile } from 'src/app/interfaces/profile.interface'
@Component({
selector: 'app-menu-profile',
templateUrl: './menu-profile.component.html',
styleUrls: ['./menu-profile.component.scss']
})
export class MenuProfileComponent implements OnInit {
@LocalStorage('profile', <Profile>{username: 'username'})
public profile!: Profile
@LocalStorage('theme', 'dark')
public theme!: string
constructor() { }
ngOnInit(): void {
}
toggleTheme () {
this.theme = this.theme == 'light' ? 'dark' : 'light'
}
}

View File

@ -3,7 +3,6 @@
</skirda-section-label>
<div class="sessions">
<skirda-session
[routerLink]="['/session/' + session.gameId]" routerLinkActive="active"
*ngFor="let session of sessions; index as i"
[session]="session"
></skirda-session>

View File

@ -23,7 +23,7 @@
.content {
flex: 1;
background-color: rgba(0,0,0,0.15);
background-color: var(--sk-right-content);
display: flex;
backdrop-filter: blur(2rem);
// overflow-y: auto;

View File

@ -12,11 +12,9 @@ import { GamesPageComponent } from './sections/games-page/games-page.component';
import { SessionPageComponent } from './sections/session-page/session-page.component';
import { GamePageComponent } from './sections/game-page/game-page.component';
import { MainRoutingModule } from './routing.module';
import { ButtonModule } from 'projects/ui/src/lib/button/button.module';
import { TypographyModule } from 'projects/ui/src/lib/typography/typography.module';
import { IconModule } from 'projects/ui/src/lib/icon/icon.module';
import { DownloadsPageComponent } from './sections/downloads-page/downloads-page.component';
import { PopupModule } from 'projects/ui/src/public-api';
import { MenuProfileComponent } from './main-menu/menu-profile/menu-profile.component'
import { SettingsPageComponent } from './sections/settings-page/settings-page.component'
@NgModule({
declarations: [
@ -30,17 +28,15 @@ import { PopupModule } from 'projects/ui/src/public-api';
SessionPageComponent,
GamePageComponent,
DownloadsPageComponent,
SettingsPageComponent,
MenuProfileComponent
],
imports: [
CommonModule,
UiModule,
ReactiveFormsModule,
FormsModule,
MainRoutingModule,
TypographyModule,
ButtonModule,
IconModule,
PopupModule,
MainRoutingModule
],
exports: [
MainRootComponent,
@ -53,6 +49,8 @@ import { PopupModule } from 'projects/ui/src/public-api';
SessionPageComponent,
GamePageComponent,
DownloadsPageComponent,
SettingsPageComponent,
MenuProfileComponent
],
})
export class MainModule {}

View File

@ -7,6 +7,7 @@ import { GamePageComponent } from './sections/game-page/game-page.component';
import { GamesPageComponent } from './sections/games-page/games-page.component';
import { ServersPageComponent } from './sections/servers-page/servers-page.component';
import { SessionPageComponent } from './sections/session-page/session-page.component';
import { SettingsPageComponent } from './sections/settings-page/settings-page.component'
const routes: Routes = [
{
@ -29,6 +30,10 @@ const routes: Routes = [
path: 'downloads',
component: DownloadsPageComponent,
},
{
path: 'settings',
component: SettingsPageComponent,
},
{
path: 'session/:id',
component: SessionPageComponent,

View File

@ -0,0 +1,8 @@
<div class="section">
<div class="section-head">
<div class="head-title">
<skirda-heading size="1">Settings</skirda-heading>
</div>
<div class="head-subtitle">Settings page will be here</div>
</div>
</div>

View File

@ -0,0 +1,7 @@
@use '../global';
:host {
display: block;
@include global.page();
}

View File

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { SettingsPageComponent } from './settings-page.component';
describe('SettingsPageComponent', () => {
let component: SettingsPageComponent;
let fixture: ComponentFixture<SettingsPageComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ SettingsPageComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(SettingsPageComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,15 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-settings-page',
templateUrl: './settings-page.component.html',
styleUrls: ['./settings-page.component.scss']
})
export class SettingsPageComponent implements OnInit {
constructor() { }
ngOnInit(): void {
}
}

View File

@ -0,0 +1,3 @@
export interface Profile {
username: string
}

View File

@ -6,4 +6,3 @@ export interface Session {
version: string;
expires: Date;
}

View File

@ -1,5 +1,5 @@
<!doctype html>
<html lang="en">
<html lang="en" data-theme="dark">
<head>
<meta charset="utf-8">
<title>GoWeb</title>

View File

@ -1,9 +1,11 @@
@use 'src/light';
@use 'src/dark';
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap');
html, body {
background-color: #222;
color: #fff;
font-size: 16px;
background-color: var(--sk-background);
color: var(--sk-text);
font-size: var(--sk-typo-base);
margin: 0;
font-family: var(--font);
line-height: 90%;
@ -14,29 +16,28 @@ button, input, textarea {
font-family: var(--font);
}
@media (prefers-color-scheme: dark) {
html:root {
@include dark.theme;
}
}
@media (prefers-color-scheme: light) {
html:root {
@include light.theme;
}
}
html[data-theme="light"]:root {
@include light.theme;
}
html[data-theme="dark"]:root {
@include dark.theme;
}
:root {
--font: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
--sk-gap-xs: 0.125rem;
--sk-gap-s: 0.25rem;
--sk-gap-m: 0.5rem;
--sk-gap-l: 0.75rem;
--sk-gap-xl: 1rem;
// colors
--sk-input: rgba(18,18,18,0.5);
--sk-input-focus: rgba(18,18,18,0.9);
--sk-primary: #d62828ff;
--sk-secondary: #f77f00ff;
// border-radius
--sk-br-s: 0.25rem;
--sk-br-m: 0.5rem;
--sk-palette-1: #ffbe0b;
--sk-palette-2: #fb5607;
--sk-palette-3: #ff006e;
--sk-palette-4: #8338ec;
--sk-palette-5: #3a86ff;
--sk-overlay: rgba(0,0,0,0.5);
}
/* Works on Firefox */