+
{{lang.phrases.slogan}}
@if (linkParams()) {
-
+
{{lang.phrases.letsgo}}
{{linkParams()?.site}}
{{'/'+linkParams()?.id | truncate}}
@@ -22,8 +26,10 @@
@defer{
-
+
+
+
+
}
@defer{
@@ -31,29 +37,18 @@
{{lang.phrases.shortTitle}}, © 2024
@for (item of social; track $index) {
-
-
-
+
+
+
}
}
+
+ @defer{ }
+
-
\ No newline at end of file
+
+ @defer{ }
+
\ No newline at end of file
diff --git a/src/app/link-parser/link-parser/link-parser.component.scss b/src/app/link-parser/link-parser/link-parser.component.scss
index 535a5fd..9fd78a6 100644
--- a/src/app/link-parser/link-parser/link-parser.component.scss
+++ b/src/app/link-parser/link-parser/link-parser.component.scss
@@ -15,7 +15,6 @@
&>div {
display: grid;
- place-content: center;
gap: 2ch;
}
}
@@ -28,44 +27,73 @@ app-text-embracer {
font-family: 'Rampart One', sans-serif;
font-size: clamp(1rem, 8vw, 5rem);
- @media (max-aspect-ratio: 1) or (max-width: 640px) {
+ -webkit-box-reflect: below 1.3rem linear-gradient(transparent 0%, #fff);
+ @media (max-width: 1080px) {
+ font-size: clamp(1rem, 7vw, 8rem);
+ }
+
+ @media ((orientation: portrait) and (max-aspect-ratio: 1)) {
font-size: clamp(1rem, 10vw, 8rem);
}
+
+
+
+ // grid-column: 2;
+}
+
+.form-wrapper {
+ grid-template-columns: auto minmax(auto, 80ch) auto;
+}
+
+form {
+ // grid-column: 2;
+ backdrop-filter: blur(var(--blur));
+ z-index: 3;
+ border-radius: .5ch;
+ max-width: 80ch;
+ width: 100%;
+ position: relative;
}
input[type=url] {
+ display: block;
font: inherit;
- padding: 1ch;
+ padding: 1.5ch 2ch;
background: transparent;
font-family: 'Courier New', Courier, monospace;
- border: 0;
- outline: 1px solid;
color: inherit;
width: 100%;
- margin: auto;
- transition: all .25s cubic-bezier(0.075, 0.82, 0.165, 1);
+ transition: all var(--t) cubic-bezier(0.075, 0.82, 0.165, 1);
border-radius: .5ch;
+ border: 2px solid #166496;
+ box-shadow: var(--shadow-elevation-medium);
+ background-color: #00274190;
+ color: #ffd60a;
+ // padding-right: 8ch;
+
+ &:focus {
+ border-color: #166496;
+ outline: unset;
+ }
}
input[type=url]::placeholder {
- opacity: 0.5;
+ color: #ffd60a;
+ opacity: 0.6;
}
+// input[type=reset] {
+// position: absolute;
+// right: 2px;
+// top: 2px;
+// height: 6ch;
+// width: 6ch;
+// }
-
-@media (prefers-reduced-motion: no-preference) {
- input[type=url]:focus {
-
- outline-offset: .5ch;
-
- }
-}
-
-// a {
-// padding: 1ch;
-// font-size: smaller;
-// color: inherit;
-// text-decoration: none;
+// @media (prefers-reduced-motion: no-preference) {
+// input[type=url]:focus {
+// outline-offset: .5ch;
+// }
// }
.site-name {
@@ -79,7 +107,7 @@ input[type=url]::placeholder {
.link {
outline: 1px solid;
border-radius: .5ch;
- transition: all cubic-bezier(0.075, 0.82, 0.165, 1) .25s;
+ transition: all cubic-bezier(0.075, 0.82, 0.165, 1) var(--t);
&:hover {
outline-offset: .5ch;
@@ -111,94 +139,17 @@ app-overlay {
}
}
-made-in-ukraine {
+made-in-ukraine, app-lang-toggle {
margin-left: auto;
-
}
-
-dialog {
- --r: 1ch;
- --b: 2px;
- padding: 0;
- max-width: min(calc(100vw - 4ch), 60ch);
- max-height: calc(100dvh - 4ch);
- border-radius: var(--r);
- border: var(--b) solid #166496;
- background-color: rgb(0, 15, 30);
- color: inherit;
-
- &::before {
- content: 'esc';
- position: fixed;
- left: 2ch;
- top: 2ch;
- display: inline-grid;
- place-content: center;
- background-color: rgba(0, 39, 65, 1);
- border: 1px solid #166496;
- height: 3.5ch;
- padding: 1ch .75ch;
- font-size: smaller;
- border-radius: .5ch;
- margin: 0 .25ch;
- font-family: monospace;
- font-weight: bold;
- line-height: 1ch;
- box-shadow: 0 .35ch #166496;
- color: #ffd60a;
- opacity: .5;
- }
-
- &::backdrop {
- backdrop-filter: blur(.5ch) brightness(.32);
- }
-
- .dialog-wrapper {
- display: grid;
- max-height: 100%;
- grid-template: auto 1fr auto / 1fr;
- }
-
- header,
- footer {
- background-color: rgb(0, 39, 65);
- padding: 1ch 2ch;
- }
-
- header {
- border-top-left-radius: calc(var(--r) - var(--b));
- border-top-right-radius: calc(var(--r) - var(--b));
-
- h2,
- h4 {
- margin: 0;
- }
- }
-
- footer {
- border-bottom-left-radius: calc(var(--r) - var(--b));
- border-bottom-right-radius: calc(var(--r) - var(--b));
- }
-
- section {
- padding: 2ch;
- overflow: auto;
- }
-
- &[open] {
- display: flex;
- }
-}
-
-
:host {
- transition: scale .1s ease-in-out;
+ transition: all var(--t) ease-in-out;
}
:host:has(dialog[open]) {
- scale: .99;
- overflow: hidden;
+ transform: scale(calc(1 - var(--scale-diff-x, .1)), calc(1 - var(--scale-diff-y, .1)));
+ filter: blur(var(--blur));
}
:host:has(input[type=url]:focus) {
diff --git a/src/app/link-parser/link-parser/link-parser.component.ts b/src/app/link-parser/link-parser/link-parser.component.ts
index 843d500..6049bd4 100644
--- a/src/app/link-parser/link-parser/link-parser.component.ts
+++ b/src/app/link-parser/link-parser/link-parser.component.ts
@@ -1,16 +1,14 @@
-import { Component, ElementRef, HostListener, Signal, ViewChild, WritableSignal, computed, effect, inject, signal } from '@angular/core';
+import { Component, HostListener, Signal, ViewChild, WritableSignal, computed, effect, inject, signal } from '@angular/core';
import { LinkParserService } from '../data-access/link-parser.service';
import { ImgurLinkParser, JsonLinkParser, MangadexLinkParser, RedditLinkParser, TelegraphLinkParser } from '../utils';
import { ActivatedRoute, Router } from '@angular/router';
import { LangService } from '../../shared/data-access/lang.service';
-import { DomManipulationService, ViewModeOption } from '../../shared/data-access';
import { Base64 } from '../../shared/utils';
import { Title } from '@angular/platform-browser';
+import { DialogComponent } from '../../shared/ui/dialog/dialog.component';
+import { LinkParserSettingsService } from '../data-access/link-parser-settings.service';
+
-const LANG_OPTIONS: ViewModeOption[] = [
- { dir: "rtl", mode: "pages", hintPhraceKey: "english", code: "en", emoji: "🇬🇧" },
- { dir: "ltr", mode: "pages", hintPhraceKey: "ukrainian", code: "uk", emoji: "🇺🇦" }
-]
@Component({
selector: 'app-link-parser',
templateUrl: './link-parser.component.html',
@@ -23,7 +21,7 @@ export class LinkParserComponent {
private title: Title = inject(Title);
private router: Router = inject(Router);
private route: ActivatedRoute = inject(ActivatedRoute);
- private dm: DomManipulationService = inject(DomManipulationService)
+ setts = inject(LinkParserSettingsService)
link: WritableSignal
= signal('');
linkParams: Signal = computed(() => this.parser.parse(this.link()));
@@ -35,10 +33,6 @@ export class LinkParserComponent {
};
});
- langOpt = LANG_OPTIONS
-
- optLangValue = () => this.langOpt.filter((opt: any) => opt.code == this.lang.lang())[0]
-
constructor(public parser: LinkParserService, public lang: LangService) {
this.initParser();
@@ -63,6 +57,7 @@ export class LinkParserComponent {
ngOnInit() {
this.initUrl()
+ this.initHotKeys()
}
async initFromclipboard() {
@@ -86,7 +81,7 @@ export class LinkParserComponent {
if (url) {
this.link.set(url ?? '')
} else {
- this.initFromclipboard();
+ if(this.setts.autoPasteLink()) this.initFromclipboard();
}
}
@@ -98,37 +93,26 @@ export class LinkParserComponent {
this.router.navigateByUrl(link);
}
- @ViewChild('dialog', { static: true }) dialogRef!: ElementRef;
- dialogElement: WritableSignal = signal(document.createElement('dialog'));
-
-
- ngAfterViewInit() {
- this.dialogElement.set(this.dialogRef.nativeElement);
- }
-
- async showHelp() {
- this.dialogElement().showModal()
- // const mutate = () => {
- // this.dialogElement().showModal()
- // }
+ @ViewChild('faqDialog') faqDialogComponent!: DialogComponent;
+ showHelp = () => this.faqDialogComponent.showDialog();
- // this.dm.startViewTransition(mutate)
- }
+ @ViewChild('settingsDialog') settingsDialogComponent!: DialogComponent;
+ showSettings = () => this.settingsDialogComponent.showDialog();
- async closeDialog(event: Event) {
- if (event.target instanceof HTMLDialogElement) {
- const mutate = () => (event.target as HTMLDialogElement).close();
-
- this.dm.startViewTransition(mutate)
- }
+ hotKeys = new Map()
+ initHotKeys() {
+ this.hotKeys.set('F1', this.showHelp)
+ this.hotKeys.set('F2', this.showSettings)
}
@HostListener('window:keydown', ["$event"])
helpHotKey(event: KeyboardEvent) {
- if (event.key === 'F1') {
+
+ if (this.hotKeys.has(event.key)) {
event.preventDefault()
- this.showHelp()
+ const f: Function = this.hotKeys.get(event.key) as Function;
+ f();
}
}
diff --git a/src/app/link-parser/ui/faq/faq.component.scss b/src/app/link-parser/ui/faq/faq.component.scss
index 372eb68..24b1578 100644
--- a/src/app/link-parser/ui/faq/faq.component.scss
+++ b/src/app/link-parser/ui/faq/faq.component.scss
@@ -1,14 +1,18 @@
+:host {
+ display: grid;
+ gap: 2ch;
+}
+
details {
padding: 1.5ch 2ch;
border-radius: .5ch;
border: 1px solid rgb(0, 40, 64);
- margin-bottom: 2ch;
- transition: all .25s ease-in-out;
+ transition: all var(--t) ease-in-out;
summary {
cursor: pointer;
font-weight: bold;
- transition: color .25s cubic-bezier(0.45, 0.05, 0.55, 0.95);
+ transition: color var(--t) cubic-bezier(0.45, 0.05, 0.55, 0.95);
}
&[open] {
diff --git a/src/app/link-parser/ui/settings/settings.component.html b/src/app/link-parser/ui/settings/settings.component.html
new file mode 100644
index 0000000..e52651a
--- /dev/null
+++ b/src/app/link-parser/ui/settings/settings.component.html
@@ -0,0 +1,28 @@
+
+
+
+
+ 🈚 {{lang.ph().language}}
+ {{lang.ph().getByKey(getLangValue(lang.lang()).hintPhraceKey)}}
+
+
{{lang.ph().settingLangDesc}}
+
+
+
+
+
+
+
+
+ @if(setts.autoPasteLink()) {
+ ON
+ } @else {
+ OFF
+ }
+
+
+ {{lang.ph().settingAutoPasteLinkDesc}}
+
+
+
+
\ No newline at end of file
diff --git a/src/app/link-parser/ui/settings/settings.component.scss b/src/app/link-parser/ui/settings/settings.component.scss
new file mode 100644
index 0000000..18c2981
--- /dev/null
+++ b/src/app/link-parser/ui/settings/settings.component.scss
@@ -0,0 +1,78 @@
+:host {
+ display: grid;
+ gap: 2ch;
+}
+
+section {
+ display: grid;
+ grid-template-columns: 1fr auto;
+ align-items: center;
+ gap: 2ch;
+ border-bottom: 2px solid #16649680;
+ padding: 0 0 1.5ch;
+
+ transition: all var(--t) ease-in-out;
+
+ &.inactive {
+ opacity: .64;
+ filter: grayscale(.8);
+ }
+
+ div {
+
+ p {
+ margin: 0;
+ opacity: .8;
+ }
+
+ .label {
+ font-weight: bold;
+ cursor: pointer;
+ }
+ }
+
+ input[type=checkbox] {
+ --q:1;
+ width: calc(5ch - 2px);
+ aspect-ratio: 1;
+ border-radius: .5ch;
+ appearance: none;
+ border: 2px solid #16649680;
+overflow: hidden;
+ position: relative;
+ transition: all var(--t) ease-in-out;
+ cursor: pointer;
+ padding: calc(var(--q) * 1ch);
+
+ &::before,
+ &::after {
+ position: absolute;
+ inset: 0;
+ display: grid;
+ place-content: center;
+ transition: all var(--t) ease-in-out;
+ }
+
+ &::before {
+ content: '❌';
+ transform: translate(0, 0%) scale(1);
+ }
+
+ &::after {
+ content: '✔️';
+ transform: translate(0, -100%) scale(0);
+ }
+
+ &:checked {
+ border-color: #166496;
+ &::before {
+ transform: translate(0, 100%) scale(0);
+ }
+
+ &::after {
+ transform: translate(0, 0%) scale(1);
+ filter: brightness(0) saturate(100%) invert(86%) sepia(42%) saturate(2655%) hue-rotate(350deg) brightness(106%) contrast(102%);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/app/link-parser/ui/settings/settings.component.ts b/src/app/link-parser/ui/settings/settings.component.ts
new file mode 100644
index 0000000..8e0dba3
--- /dev/null
+++ b/src/app/link-parser/ui/settings/settings.component.ts
@@ -0,0 +1,23 @@
+import { Component, WritableSignal, effect, inject, signal } from '@angular/core';
+import { LinkParserSettingsService } from '../../data-access/link-parser-settings.service';
+import { LangService } from '../../../shared/data-access/lang.service';
+
+@Component({
+ selector: 'app-settings',
+ templateUrl: './settings.component.html',
+ styleUrl: './settings.component.scss'
+})
+export class SettingsComponent {
+ setts = inject(LinkParserSettingsService)
+ lang = inject(LangService)
+
+
+ getLangValue(lang: string) {
+ return this.lang.langOpt.filter((opt: any) => opt.code == lang)[0]
+ }
+
+ setAutoPasteLink(e: Event) {
+ this.setts.setAutoPasteLink((e.target as HTMLInputElement).checked)
+ }
+
+}
diff --git a/src/app/shared/data-access/lang.service.ts b/src/app/shared/data-access/lang.service.ts
index 9690133..3977f74 100644
--- a/src/app/shared/data-access/lang.service.ts
+++ b/src/app/shared/data-access/lang.service.ts
@@ -1,9 +1,13 @@
-import { Injectable, WritableSignal, signal } from '@angular/core';
+import { ChangeDetectorRef, Injectable, WritableSignal, inject, signal } from '@angular/core';
import { Phrases } from '../utils/phrases';
import { Observable, map, of } from 'rxjs';
import { HttpClient } from '@angular/common/http';
+import { ViewModeOption } from './viewer.service';
-type Langs = { en: string, uk: string }
+const LANG_OPTIONS: ViewModeOption[] = [
+ { dir: "rtl", mode: "pages", hintPhraceKey: "english", code: "en", emoji: "🇬🇧" },
+ { dir: "ltr", mode: "pages", hintPhraceKey: "ukrainian", code: "uk", emoji: "🇺🇦" }
+]
const DEFAULT_LANG = 'en'
const LANG_STORAGE_NAME = 'lang'
@@ -18,6 +22,8 @@ export class LangService {
['uk', "manifest-uk.webmanifest"]
]);
+ langOpt = LANG_OPTIONS
+
lang: WritableSignal = signal(localStorage.getItem(LANG_STORAGE_NAME) ?? DEFAULT_LANG);
linkManifestElement: WritableSignal = signal(document.querySelector('link[rel="manifest"]'))
@@ -33,7 +39,7 @@ export class LangService {
this.lang.set(lang)
document.documentElement.lang = lang
localStorage.setItem(LANG_STORAGE_NAME, lang)
- this.updateTranslate()
+ this.updateTranslate();
}
updateManifest() {
diff --git a/src/app/shared/data-access/viewer.service.ts b/src/app/shared/data-access/viewer.service.ts
index e4393c7..d6f508b 100644
--- a/src/app/shared/data-access/viewer.service.ts
+++ b/src/app/shared/data-access/viewer.service.ts
@@ -9,9 +9,9 @@ export interface ViewModeOption {
}
export const VIEV_MODE_OPTIONS: ViewModeOption[] = [
- { dir: "rtl", mode: "pages", hintPhraceKey: "scrollLeft", code: "", emoji: "⬅️" },
- { dir: "ltr", mode: "pages", hintPhraceKey: "scrollRight", code: "", emoji: "➡️" },
- { dir: "ltr", mode: "long", hintPhraceKey: "scrollDown", code: "", emoji: "⬇️" },
+ { dir: "rtl", mode: "pages", hintPhraceKey: "scrollLeft", code: "1", emoji: "⬅️" },
+ { dir: "ltr", mode: "pages", hintPhraceKey: "scrollRight", code: "2", emoji: "➡️" },
+ { dir: "ltr", mode: "long", hintPhraceKey: "scrollDown", code: "3", emoji: "⬇️" },
]
@Injectable({
diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts
index 1ec78ec..3356595 100644
--- a/src/app/shared/shared.module.ts
+++ b/src/app/shared/shared.module.ts
@@ -12,6 +12,8 @@ import { WarmControlComponent } from './ui/warm-control/warm-control.component';
import { PagesIndicatorComponent } from './ui/pages-indicator/pages-indicator.component';
import { NsfwWarningComponent } from './ui/nsfw-warning/nsfw-warning.component';
import { MadeInUkraineComponent } from './ui/made-in-ukraine/made-in-ukraine.component';
+import { DialogComponent } from './ui/dialog/dialog.component';
+import { LangToggleComponent } from './ui/lang-toggle/lang-toggle.component';
@@ -26,13 +28,15 @@ import { MadeInUkraineComponent } from './ui/made-in-ukraine/made-in-ukraine.com
WarmControlComponent,
PagesIndicatorComponent,
NsfwWarningComponent,
- MadeInUkraineComponent
+ MadeInUkraineComponent,
+ DialogComponent,
+ LangToggleComponent
],
imports: [
CommonModule,
FormsModule,
RouterModule
],
- exports: [TruncatePipe, TextEmbracerComponent, ViewerComponent, OverlayComponent, ViewModeBarComponent, MadeInUkraineComponent]
+ exports: [TruncatePipe, TextEmbracerComponent, ViewerComponent, OverlayComponent, ViewModeBarComponent, MadeInUkraineComponent, DialogComponent, LangToggleComponent]
})
export class SharedModule { }
diff --git a/src/app/shared/ui/dialog/dialog.component.html b/src/app/shared/ui/dialog/dialog.component.html
new file mode 100644
index 0000000..b6ffa13
--- /dev/null
+++ b/src/app/shared/ui/dialog/dialog.component.html
@@ -0,0 +1,26 @@
+
\ No newline at end of file
diff --git a/src/app/shared/ui/dialog/dialog.component.scss b/src/app/shared/ui/dialog/dialog.component.scss
new file mode 100644
index 0000000..5bf64b1
--- /dev/null
+++ b/src/app/shared/ui/dialog/dialog.component.scss
@@ -0,0 +1,69 @@
+
+:host {
+ position: absolute;
+}
+
+dialog {
+ --r: 1ch;
+ --b: 2px;
+ padding: 0;
+ max-width: min(calc(100vw - 4ch), 60ch);
+ max-height: calc(100dvh - 8ch);
+ border-radius: var(--r);
+ border: var(--b) solid #166496;
+ background-color: rgb(0, 15, 30);
+ color: inherit;
+ margin-top: 4ch;
+
+ &::backdrop {
+ backdrop-filter: brightness(.8);
+ }
+
+ .dialog-wrapper {
+ display: grid;
+ max-height: 100%;
+ grid-template: auto 1fr auto / 1fr;
+ }
+
+ header,
+ footer {
+ background-color: rgb(0, 39, 65);
+ padding: 2ch;
+ border: 0 solid #166496;
+ }
+
+ header {
+ border-top-left-radius: calc(var(--r) - var(--b));
+ border-top-right-radius: calc(var(--r) - var(--b));
+ border-bottom-width: 2px;
+ display: grid;
+ grid-template-columns: 1fr auto;
+ align-items: center;
+
+ h2,
+ h4 {
+ margin: 0;
+ }
+
+ &:has(form) {
+ padding: .5ch 2ch;
+ gap: 1ch;
+ }
+ }
+
+ footer {
+ border-top-width: 2px;
+ border-bottom-left-radius: calc(var(--r) - var(--b));
+ border-bottom-right-radius: calc(var(--r) - var(--b));
+ }
+
+ section {
+ padding: 2ch;
+ overflow: auto;
+ }
+
+ &[open] {
+ display: flex;
+ box-shadow: var(--shadow-elevation-high);
+ }
+}
diff --git a/src/app/shared/ui/dialog/dialog.component.ts b/src/app/shared/ui/dialog/dialog.component.ts
new file mode 100644
index 0000000..33c8846
--- /dev/null
+++ b/src/app/shared/ui/dialog/dialog.component.ts
@@ -0,0 +1,33 @@
+import { Component, ElementRef, Input, ViewChild, WritableSignal, inject, signal } from '@angular/core';
+import { LangService } from '../../data-access/lang.service';
+
+@Component({
+ selector: 'app-dialog',
+ templateUrl: './dialog.component.html',
+ styleUrl: './dialog.component.scss'
+})
+export class DialogComponent {
+ lang = inject(LangService)
+
+ @Input() title: string = 'Dialog Title'
+ @Input() footer: boolean = false
+ @Input() closeHeaderButton: boolean = true
+
+ @ViewChild('dialog', { static: true }) dialogRef!: ElementRef;
+ dialogElement: WritableSignal = signal(document.createElement('dialog'));
+
+
+ ngAfterViewInit() {
+ this.dialogElement.set(this.dialogRef.nativeElement);
+ }
+
+ closeDialog(event: Event) {
+ if (event.target instanceof HTMLDialogElement) {
+ (event.target as HTMLDialogElement).close();
+ }
+ }
+
+ showDialog() {
+ this.dialogElement().showModal()
+ }
+}
diff --git a/src/app/shared/ui/lang-toggle/lang-toggle.component.html b/src/app/shared/ui/lang-toggle/lang-toggle.component.html
new file mode 100644
index 0000000..c996a76
--- /dev/null
+++ b/src/app/shared/ui/lang-toggle/lang-toggle.component.html
@@ -0,0 +1,2 @@
+
\ No newline at end of file
diff --git a/src/app/shared/ui/lang-toggle/lang-toggle.component.scss b/src/app/shared/ui/lang-toggle/lang-toggle.component.scss
new file mode 100644
index 0000000..e69de29
diff --git a/src/app/shared/ui/lang-toggle/lang-toggle.component.ts b/src/app/shared/ui/lang-toggle/lang-toggle.component.ts
new file mode 100644
index 0000000..6bb4a2b
--- /dev/null
+++ b/src/app/shared/ui/lang-toggle/lang-toggle.component.ts
@@ -0,0 +1,18 @@
+import { Component, WritableSignal, inject, signal } from '@angular/core';
+import { LangService } from '../../data-access/lang.service';
+import { ViewModeOption } from '../../data-access';
+
+
+
+@Component({
+ selector: 'app-lang-toggle',
+ templateUrl: './lang-toggle.component.html',
+ styleUrl: './lang-toggle.component.scss'
+})
+export class LangToggleComponent {
+ lang = inject(LangService)
+
+ seed: WritableSignal = signal((Math.ceil(Math.random() * 1000)).toString())
+
+ optLangValue = () => this.lang.langOpt.filter((opt: any) => opt.code == this.lang.lang())[0]
+}
diff --git a/src/app/shared/ui/made-in-ukraine/made-in-ukraine.component.ts b/src/app/shared/ui/made-in-ukraine/made-in-ukraine.component.ts
index cc63712..bcdf4c9 100644
--- a/src/app/shared/ui/made-in-ukraine/made-in-ukraine.component.ts
+++ b/src/app/shared/ui/made-in-ukraine/made-in-ukraine.component.ts
@@ -2,7 +2,7 @@ import { Component } from '@angular/core';
@Component({
selector: 'made-in-ukraine',
- template: `Made in
+ template: `Made©in
Ukraine`,
styles: [`
:host {
@@ -12,7 +12,7 @@ import { Component } from '@angular/core';
display: grid;
text-transform: uppercase;
opacity: .4;
- transition: opacity .25s ease-in-out;
+ transition: opacity var(--t) ease-in-out;
&:hover {
opacity: 1;
@@ -24,3 +24,4 @@ import { Component } from '@angular/core';
export class MadeInUkraineComponent {
}
+// ♆
\ No newline at end of file
diff --git a/src/app/shared/ui/overlay/overlay.component.scss b/src/app/shared/ui/overlay/overlay.component.scss
index f6ae92b..2e02392 100644
--- a/src/app/shared/ui/overlay/overlay.component.scss
+++ b/src/app/shared/ui/overlay/overlay.component.scss
@@ -12,7 +12,7 @@
z-index: 1;
padding: 1ch 1rem;
pointer-events: none;
- transition: opacity .1s cubic-bezier(.4, 0, 1, 1);
+ transition: opacity var(--t) cubic-bezier(.4, 0, 1, 1);
font-size: 14px;
line-height: 1;
diff --git a/src/app/shared/ui/pages-indicator/pages-indicator.component.scss b/src/app/shared/ui/pages-indicator/pages-indicator.component.scss
index 8bf03b3..b3e1409 100644
--- a/src/app/shared/ui/pages-indicator/pages-indicator.component.scss
+++ b/src/app/shared/ui/pages-indicator/pages-indicator.component.scss
@@ -15,7 +15,7 @@ div {
cursor: pointer;
font-weight: bold;
line-height: 1;
- transition: all .1s ease-in-out;
+ transition: all var(--t) ease-in-out;
&:hover, &.active {
color: #ffd60a;
diff --git a/src/app/shared/ui/text-embracer/text-embracer.component.scss b/src/app/shared/ui/text-embracer/text-embracer.component.scss
index ec51f2d..fbc3216 100644
--- a/src/app/shared/ui/text-embracer/text-embracer.component.scss
+++ b/src/app/shared/ui/text-embracer/text-embracer.component.scss
@@ -9,7 +9,6 @@
line-height: 1;
display: flex;
gap: .1ch;
-
& > span {
aspect-ratio: 2/3;
@@ -17,8 +16,6 @@
border: var(--border-width) var(--border-style) var(--border-color);
border-radius: .1ch;
height: 1.3lh;
-
place-content: center;
-
}
}
\ No newline at end of file
diff --git a/src/app/shared/ui/view-mode-bar/view-mode-bar.component.html b/src/app/shared/ui/view-mode-bar/view-mode-bar.component.html
index c9fadff..6247ab9 100644
--- a/src/app/shared/ui/view-mode-bar/view-mode-bar.component.html
+++ b/src/app/shared/ui/view-mode-bar/view-mode-bar.component.html
@@ -1,11 +1,12 @@
@for(option of options; track option.emoji) {
-
+
-
-}
\ No newline at end of file
+}
+
diff --git a/src/app/shared/ui/view-mode-bar/view-mode-bar.component.ts b/src/app/shared/ui/view-mode-bar/view-mode-bar.component.ts
index eb8f25b..95016c0 100644
--- a/src/app/shared/ui/view-mode-bar/view-mode-bar.component.ts
+++ b/src/app/shared/ui/view-mode-bar/view-mode-bar.component.ts
@@ -9,6 +9,7 @@ import { LangService } from '../../data-access/lang.service';
export class ViewModeBarComponent {
@Input() options: any;
@Input() value: any;
+ @Input() seed: string = 'seed';
@Output() valueChange = new EventEmitter();
diff --git a/src/app/shared/ui/viewer/viewer.component.html b/src/app/shared/ui/viewer/viewer.component.html
index b886135..14fd2fd 100644
--- a/src/app/shared/ui/viewer/viewer.component.html
+++ b/src/app/shared/ui/viewer/viewer.component.html
@@ -62,7 +62,7 @@
🏠 {{separator}}{{episode?.title}}
+ [value]="viewer.viewModeOption.code" />
\ No newline at end of file
diff --git a/src/app/shared/ui/viewer/viewer.component.scss b/src/app/shared/ui/viewer/viewer.component.scss
index 3560ce7..f4625a1 100644
--- a/src/app/shared/ui/viewer/viewer.component.scss
+++ b/src/app/shared/ui/viewer/viewer.component.scss
@@ -40,7 +40,7 @@ figure {
&.nsfw {
filter: blur(4rem) brightness(.1);
- transition: all .25s ease-in-out;
+ transition: all var(--t) ease-in-out;
}
&.nsfw.show {
filter: blur(0) brightness(1);
diff --git a/src/app/shared/ui/warm-control/warm-control.component.scss b/src/app/shared/ui/warm-control/warm-control.component.scss
index 2f94ced..8d3ce3f 100644
--- a/src/app/shared/ui/warm-control/warm-control.component.scss
+++ b/src/app/shared/ui/warm-control/warm-control.component.scss
@@ -14,7 +14,7 @@ input[type=range][orient=vertical] {
width: 8px;
height: 0;
padding: 0 5px;
- transition: all .25s ease-in-out;
+ transition: all var(--t) ease-in-out;
opacity: 0;
pointer-events: none;
diff --git a/src/app/shared/utils/phrases.ts b/src/app/shared/utils/phrases.ts
index 6a4148a..0bb32c8 100644
--- a/src/app/shared/utils/phrases.ts
+++ b/src/app/shared/utils/phrases.ts
@@ -32,6 +32,11 @@ export class Phrases {
yesYouCanPasteJsonLink = "Yes, you can, for example, to {value} or to your website."
howToUseChytankaAnswer = "🔗 Just paste the link to the episode into the input field.
🔳 If the link is supported, a button will appear,
🖱️ click it,
📖 and read easily and comfortably! 🛋️"
whereIdIs = ", where {id} is the unique identifier of the post."
+ language = "Language"
+ settingLangDesc = "Change the user interface language."
+ settings = "Settings"
+ autoPasteLink = "Auto Paste Link"
+ settingAutoPasteLinkDesc = "Automatically inserts a link from the clipboard into the input field."
getByKey = (key: string) => (Object.keys(this).includes(key)) ? this[key as keyof Phrases] : null;
diff --git a/src/assets/langs/uk.json b/src/assets/langs/uk.json
index b91038a..0abcfb0 100644
--- a/src/assets/langs/uk.json
+++ b/src/assets/langs/uk.json
@@ -29,5 +29,10 @@
"whatJsonModel": "Якою має бути модель JSON файлу?",
"yesYouCanPasteJsonLink": "Так, можна, наприклад, на {value} або на власний сайт.",
"howToUseChytankaAnswer" : "🔗 Просто встав посилання на епізод у поле введення.
🔳Якщо посилання підтримується, з'явиться кнопка,
🖱️ натисни її,
📖 і читай легко та зручно 🛋️.",
- "whereIdIs": ", де {id} — унікальний ідентифікатор допису."
+ "whereIdIs": ", де {id} — унікальний ідентифікатор допису.",
+ "language": "Мова",
+ "settingLangDesc": "Змінити мову інтерфейсу користувача.",
+ "settings": "Налаштування",
+ "autoPasteLink": "Автоматична вставка посилання",
+ "settingAutoPasteLinkDesc": "Автоматично вставляти посилання з буфера обміну в поле для введення."
}
\ No newline at end of file
diff --git a/src/styles.scss b/src/styles.scss
index 7c2a540..e8cc937 100644
--- a/src/styles.scss
+++ b/src/styles.scss
@@ -1,6 +1,8 @@
@import url('https://fonts.googleapis.com/css2?family=EB+Garamond:ital,wght@1,500&family=Rampart+One&display=swap');
:root {
+ --blur: 1ch;
+ --t: 266.666667ms; //calc(1s / 60 * 16);
--surface: hsl(203.44 8% 16%);
--text: hsl(200 5% 80%);
color-scheme: light dark;
@@ -38,10 +40,11 @@ body {
border-radius: .5ch;
cursor: pointer;
background: hsl(203.44 8% 16%);
- transition: all .25s;
+ transition: all var(--t);
text-wrap: balance;
-webkit-tap-highlight-color: transparent;
- box-shadow: 0 .1ch .2ch #0004;
+ // box-shadow: 0 .1ch .2ch #0004;
+ box-shadow: var(--shadow-elevation-low);
&.small {
--q: 0.5
@@ -72,21 +75,31 @@ body {
&:hover {
background-color: #166496;
color: #ffd60a;
+ box-shadow: var(--shadow-elevation-medium);
}
&:active {
background-color: rgba(0, 15, 30, 1);
+ box-shadow: unset;
}
&.full {
width: 100%;
}
+
+ &:disabled {
+ cursor: not-allowed;
+ // pointer-events: none;
+ filter: grayscale(.64);
+ box-shadow: unset;
+ background-color: rgba(0, 15, 30, .5);
+ }
}
a {
color: #4081b1;
text-decoration: none;
- transition: all .25s ease-in-out;
+ transition: all var(--t) ease-in-out;
&:hover {
color: #ffd60a;
@@ -98,4 +111,58 @@ a[target="_blank"]:after {
font-size: 0.8em;
opacity: .8;
vertical-align: super;
+}
+
+:root {
+ // https://www.joshwcomeau.com/shadow-palette/
+ --shadow-color: 200deg 12% 5%;
+ --shadow-elevation-low:
+ 0px 0.4px 0.4px hsl(var(--shadow-color) / 0.72),
+ 0px 0.6px 0.5px -2px hsl(var(--shadow-color) / 0.54),
+ 0px 1.8px 1.6px -4px hsl(var(--shadow-color) / 0.36);
+ --shadow-elevation-medium:
+ 0px 0.4px 0.4px hsl(var(--shadow-color) / 0.76),
+ 0px 1px 0.9px -1.3px hsl(var(--shadow-color) / 0.61),
+ -0.1px 3.2px 2.9px -2.7px hsl(var(--shadow-color) / 0.47),
+ -0.2px 9px 8.1px -4px hsl(var(--shadow-color) / 0.33);
+ --shadow-elevation-high:
+ 0px 0.4px 0.4px hsl(var(--shadow-color) / 0.62),
+ 0px 1.3px 1.2px -0.5px hsl(var(--shadow-color) / 0.57),
+ -0.1px 2.6px 2.3px -1px hsl(var(--shadow-color) / 0.52),
+ -0.1px 4.8px 4.3px -1.5px hsl(var(--shadow-color) / 0.47),
+ -0.2px 8.6px 7.7px -2px hsl(var(--shadow-color) / 0.42),
+ -0.4px 14.5px 13.1px -2.5px hsl(var(--shadow-color) / 0.36),
+ -0.6px 23.2px 20.9px -3px hsl(var(--shadow-color) / 0.31),
+ -0.9px 35.1px 31.6px -3.5px hsl(var(--shadow-color) / 0.26),
+ -1.4px 51px 45.9px -4px hsl(var(--shadow-color) / 0.21);
+ }
+
+@supports (transition-behavior: allow-discrete) {
+
+ dialog,
+ dialog::backdrop {
+ transition-property: opacity, transform, overlay, display, filter;
+ transition-duration: var(--t);
+ transition-behavior: allow-discrete;
+ opacity: 0;
+ transform: scale(calc(1 + var(--scale-diff-x, .1)), calc(1 + var(--scale-diff-y, .1)));
+ filter: blur(var(--blur));
+ }
+
+ dialog[open],
+ dialog[open]::backdrop {
+ opacity: 1;
+ transform: scale(1);
+ filter: blur(0);
+ }
+
+ @starting-style {
+
+ dialog[open],
+ dialog[open]::backdrop {
+ opacity: 0;
+ transform: scale(calc(1 + var(--scale-diff-x, .1)), calc(1 + var(--scale-diff-y, .1)));
+ filter: blur(var(--blur));
+ }
+ }
}
\ No newline at end of file