🧭 Add navigation sidebar and 📏 enforce minimum window dimensions
- Add sidebar component to enhance site navigation and improve user experience - Implement window size constraints (min 1366x768) to ensure proper display across devices
This commit is contained in:
parent
aa8d8bb4f3
commit
bf3c950da0
12 changed files with 377 additions and 20 deletions
|
@ -2,6 +2,7 @@ import { LitElement, css, html } from 'lit';
|
|||
import { customElement, property } from 'lit/decorators.js';
|
||||
|
||||
import '@components/EveLink';
|
||||
import { ifDefined } from 'lit/directives/if-defined.js';
|
||||
import { when } from 'lit/directives/when.js';
|
||||
|
||||
@customElement('arx-app-icon')
|
||||
|
@ -18,6 +19,12 @@ export class AppIcon extends LitElement {
|
|||
@property()
|
||||
name = 'App';
|
||||
|
||||
@property({ type: Boolean })
|
||||
small = false;
|
||||
|
||||
@property({ type: Boolean })
|
||||
selected = false;
|
||||
|
||||
private iconElement?: HTMLElement;
|
||||
|
||||
static override styles = [
|
||||
|
@ -100,6 +107,12 @@ export class AppIcon extends LitElement {
|
|||
}
|
||||
}
|
||||
|
||||
:host([small]) .icon {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.icon-svg {
|
||||
color: white;
|
||||
filter: drop-shadow(
|
||||
|
@ -130,6 +143,15 @@ export class AppIcon extends LitElement {
|
|||
opacity: 0.85;
|
||||
}
|
||||
|
||||
:host([selected]) .app-name {
|
||||
color: var(--color-primary-content);
|
||||
}
|
||||
|
||||
:host([small]) .app-name {
|
||||
font-size: 14px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.app-icon:hover .icon {
|
||||
transform: scale(1.05) translateZ(10px) rotateX(var(--rotate-x, 0deg)) rotateY(var(--rotate-y, 0deg));
|
||||
box-shadow:
|
||||
|
@ -137,6 +159,10 @@ export class AppIcon extends LitElement {
|
|||
0 0 0 1px rgba(255, 255, 255, 0.15);
|
||||
}
|
||||
|
||||
:host([small]) .app-icon:hover .icon {
|
||||
transform: scale(1.05) translateZ(5px) rotateX(var(--rotate-x, 0deg)) rotateY(var(--rotate-y, 0deg));
|
||||
}
|
||||
|
||||
.app-icon:hover .app-name {
|
||||
opacity: 1;
|
||||
transform: translateY(2px) scale(1.02);
|
||||
|
@ -146,6 +172,10 @@ export class AppIcon extends LitElement {
|
|||
transform: scale(1.08) translateZ(15px);
|
||||
}
|
||||
|
||||
:host([small]) .app-icon:hover .icon-svg {
|
||||
transform: scale(1.08) translateZ(8px);
|
||||
}
|
||||
|
||||
.app-icon:active .icon {
|
||||
transform: scale(var(--tap-scale)) translateZ(0);
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, calc(var(--shadow-opacity) * 0.8));
|
||||
|
@ -222,10 +252,10 @@ export class AppIcon extends LitElement {
|
|||
this.icon,
|
||||
() =>
|
||||
html`<iconify-icon
|
||||
icon="${this.icon}"
|
||||
icon="${ifDefined(this.icon)}"
|
||||
class="icon-svg"
|
||||
width="64"
|
||||
height="64"
|
||||
width="${this.small ? '48' : '64'}"
|
||||
height="${this.small ? '48' : '64'}"
|
||||
></iconify-icon>`,
|
||||
)}
|
||||
</div>
|
||||
|
|
|
@ -35,10 +35,6 @@ export class Header extends LitElement {
|
|||
header {
|
||||
background: var(--color-primary);
|
||||
backdrop-filter: blur(10px);
|
||||
border: var(--border) solid var(--color-primary-content);
|
||||
box-shadow: calc(var(--depth) * 4px) calc(var(--depth) * 4px)
|
||||
calc(var(--depth) * 8px)
|
||||
oklch(from var(--color-base-content) l c h / 0.2);
|
||||
height: var(--font-2xl, 3rem);
|
||||
font-size: var(--font-md, 1rem);
|
||||
transition: all 0.3s ease;
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import defaultAvatar from '@assets/default-avatar.png';
|
||||
import type { NDKUserProfile } from '@nostr-dev-kit/ndk';
|
||||
import { LitElement, css, html } from 'lit-element';
|
||||
import { customElement, property } from 'lit/decorators.js';
|
||||
type AvatarSize = 'short' | 'medium' | 'large' | 'huge';
|
||||
import defaultAvatar from '@/default-avatar.png';
|
||||
|
||||
@customElement('arx-nostr-avatar')
|
||||
export class ArxNostrAvatar extends LitElement {
|
||||
|
|
267
src/components/Sidebar.ts
Normal file
267
src/components/Sidebar.ts
Normal file
|
@ -0,0 +1,267 @@
|
|||
import defaultAvatar from '@assets/default-avatar.png';
|
||||
import logo from '@assets/logo.png';
|
||||
import type { NDKUserProfile } from '@nostr-dev-kit/ndk';
|
||||
import { LitElement, css, html } from 'lit';
|
||||
import { customElement, property } from 'lit/decorators.js';
|
||||
import { map } from 'lit/directives/map.js';
|
||||
|
||||
@customElement('arx-sidebar')
|
||||
export default class Sidebar extends LitElement {
|
||||
@property({ type: String })
|
||||
currentPath = '';
|
||||
|
||||
@property({ type: String })
|
||||
userNpub = '';
|
||||
|
||||
@property({ type: Object })
|
||||
userProfile: NDKUserProfile | undefined = undefined;
|
||||
|
||||
@property({ type: Array })
|
||||
apps = [
|
||||
{
|
||||
id: 1,
|
||||
href: 'beacon',
|
||||
name: 'Beacon',
|
||||
color: '#FF8C00',
|
||||
icon: 'fa-solid:sun',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
href: 'arbor',
|
||||
name: 'Arbor',
|
||||
color: '#FF4040',
|
||||
icon: 'fa-solid:tree',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
href: 'wallet',
|
||||
name: 'Wallet',
|
||||
color: '#1E90FF',
|
||||
icon: 'fa-solid:spa',
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
href: 'settings',
|
||||
name: 'Settings',
|
||||
color: '#7B68EE',
|
||||
icon: 'fa-solid:tools',
|
||||
},
|
||||
];
|
||||
|
||||
static override styles = css`
|
||||
:host {
|
||||
overflow-x: hidden;
|
||||
background: var(--color-base-200);
|
||||
padding: 0 0 1.5rem 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
overflow-y: overlay;
|
||||
scrollbar-width: none;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
margin: 3px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: rgba(100, 100, 100, 0.4);
|
||||
border-radius: 10px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: rgba(100, 100, 100, 0.7);
|
||||
}
|
||||
|
||||
:host:hover {
|
||||
scrollbar-color: rgba(100, 100, 100, 0.4) transparent;
|
||||
}
|
||||
|
||||
:host::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
width: 10px;
|
||||
height: 100%;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
background: linear-gradient(to right, transparent, rgba(0, 0, 0, 0.03));
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
:host:hover::after {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.app-icon-small {
|
||||
margin-bottom: 1.5rem;
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
.logo {
|
||||
height: 64px;
|
||||
margin-bottom: 1.5rem;
|
||||
cursor: pointer;
|
||||
transition: transform 0.2s ease, filter 0.3s ease;
|
||||
border-radius: var(--radius-field);
|
||||
padding: 8px;
|
||||
background: none;
|
||||
filter:
|
||||
drop-shadow(0 0 2px rgba(0, 0, 0, 1))
|
||||
drop-shadow(0 0 1px rgba(255, 255, 255, 1));
|
||||
}
|
||||
|
||||
.logo-container {
|
||||
width: 100%;
|
||||
background: var(--color-accent);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 12px 0;
|
||||
margin-bottom: 1.5rem;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.logo-container::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: -5px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 6px;
|
||||
background: rgba(0, 0, 0, 0.08);
|
||||
border-radius: 50%;
|
||||
filter: blur(3px);
|
||||
}
|
||||
|
||||
.logo-container .logo {
|
||||
margin-bottom: 0;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.logo:hover {
|
||||
transform: translateY(-2px) scale(1.02);
|
||||
filter:
|
||||
drop-shadow(0 0 3px rgba(0, 0, 0, 0.8))
|
||||
drop-shadow(0 0 2px rgba(255, 255, 255, 0.6));
|
||||
}
|
||||
|
||||
.logo:active {
|
||||
transform: translateY(1px) scale(0.98);
|
||||
filter:
|
||||
drop-shadow(0 0 1px rgba(0, 0, 0, 0.6));
|
||||
transition-duration: 0.1s;
|
||||
}
|
||||
|
||||
.app-icon-small.active {
|
||||
transform: scale(1.15);
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.app-icon-small.active::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -8px;
|
||||
left: -8px;
|
||||
right: -8px;
|
||||
bottom: -8px;
|
||||
background: var(--color-accent);
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.sidebar-item {
|
||||
position: relative;
|
||||
margin-bottom: 1.5rem;
|
||||
transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||
}
|
||||
|
||||
.sidebar-item:hover {
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.profile-avatar {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||
border: 2px solid var(--color-base-300);
|
||||
}
|
||||
|
||||
.profile-avatar.active {
|
||||
border-color: var(--color-primary);
|
||||
transform: scale(1.15);
|
||||
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.profile-item {
|
||||
margin-bottom: 1.5rem;
|
||||
transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.profile-item:hover .profile-avatar {
|
||||
transform: translateY(-2px) scale(1.05);
|
||||
}
|
||||
`;
|
||||
|
||||
private handleLogoClick() {
|
||||
this.dispatchEvent(new CustomEvent('navigate', { detail: 'home' }));
|
||||
}
|
||||
|
||||
override render() {
|
||||
const activePath = this.currentPath.split('/')[0];
|
||||
|
||||
return html`
|
||||
<div class="sidebar-item logo-container">
|
||||
<img
|
||||
src="${logo}"
|
||||
alt="Eve"
|
||||
class="logo"
|
||||
@click=${this.handleLogoClick}
|
||||
/>
|
||||
</div>
|
||||
${map(
|
||||
this.apps,
|
||||
(app) => html`
|
||||
<div class="sidebar-item">
|
||||
<arx-app-icon
|
||||
class="app-icon-small ${activePath === app.href.split('/')[0] ? 'active' : ''}"
|
||||
?selected=${activePath === app.href.split('/')[0]}
|
||||
.icon=${app.icon}
|
||||
.color=${app.color}
|
||||
.href=${`#${app.href}`}
|
||||
.name=${app.name}
|
||||
?small=${true}
|
||||
></arx-app-icon>
|
||||
</div>
|
||||
`,
|
||||
)}
|
||||
<div style="flex-grow: 1;"></div>
|
||||
<div class="profile-item">
|
||||
<a href="#profile/${this.userNpub}" class="profile-link">
|
||||
<img
|
||||
class="profile-avatar ${activePath === 'profile' ? 'active' : ''}"
|
||||
src=${this.userProfile?.picture || defaultAvatar}
|
||||
alt="Profile"
|
||||
@error=${(e: Event) => {
|
||||
(e.target as HTMLImageElement).src = defaultAvatar;
|
||||
}}
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue