desktop: components/start-menu — Windows 11-style centered start menu with app grid, search, animated slide-up
This commit is contained in:
parent
490317b3ea
commit
8ad129ed17
@ -0,0 +1,24 @@
|
||||
<div class="start-menu-backdrop" (click)="close.emit()"></div>
|
||||
<div class="start-menu">
|
||||
<div class="start-header">
|
||||
<span class="start-logo">🦋</span>
|
||||
<span class="start-title">Butterfly</span>
|
||||
</div>
|
||||
|
||||
<div class="start-search">
|
||||
<input type="text" placeholder="Type to search..." class="search-input" />
|
||||
</div>
|
||||
|
||||
<div class="app-grid">
|
||||
@for (app of apps; track app.id) {
|
||||
<button class="app-tile" (click)="onLaunch(app.id)">
|
||||
<span class="app-tile-icon">{{ app.icon }}</span>
|
||||
<span class="app-tile-title">{{ app.title }}</span>
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="start-footer">
|
||||
<span class="version">Butterfly Desktop v0.1</span>
|
||||
</div>
|
||||
</div>
|
||||
134
desktop/src/app/components/start-menu/start-menu.component.scss
Normal file
134
desktop/src/app/components/start-menu/start-menu.component.scss
Normal file
@ -0,0 +1,134 @@
|
||||
:host {
|
||||
display: contents;
|
||||
}
|
||||
|
||||
.start-menu-backdrop {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 8000;
|
||||
}
|
||||
|
||||
.start-menu {
|
||||
position: fixed;
|
||||
bottom: 56px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 520px;
|
||||
max-height: 580px;
|
||||
background: #2d2d2d;
|
||||
border: 1px solid #444;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 8px 48px rgba(0, 0, 0, 0.5), 0 2px 12px rgba(0, 0, 0, 0.3);
|
||||
z-index: 8500;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
animation: slideUp 0.2s ease-out;
|
||||
}
|
||||
|
||||
@keyframes slideUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(-50%) translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateX(-50%) translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.start-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 20px 24px 12px;
|
||||
}
|
||||
|
||||
.start-logo {
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.start-title {
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
color: #e0e0e0;
|
||||
}
|
||||
|
||||
.start-search {
|
||||
padding: 0 20px 12px;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
width: 100%;
|
||||
height: 36px;
|
||||
padding: 0 12px;
|
||||
border: 1px solid #555;
|
||||
border-radius: 6px;
|
||||
background: #383838;
|
||||
color: #e0e0e0;
|
||||
font-size: 13px;
|
||||
outline: none;
|
||||
box-sizing: border-box;
|
||||
|
||||
&::placeholder {
|
||||
color: #888;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
border-color: #0078d4;
|
||||
box-shadow: 0 0 0 1px #0078d4;
|
||||
}
|
||||
}
|
||||
|
||||
.app-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 4px;
|
||||
padding: 4px 20px 16px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.app-tile {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 16px 8px;
|
||||
border: none;
|
||||
background: transparent;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
transition: background 0.15s;
|
||||
color: #e0e0e0;
|
||||
|
||||
&:hover {
|
||||
background: rgba(255, 255, 255, 0.06);
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
}
|
||||
}
|
||||
|
||||
.app-tile-icon {
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
.app-tile-title {
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.start-footer {
|
||||
padding: 8px 24px 14px;
|
||||
border-top: 1px solid #3a3a3a;
|
||||
}
|
||||
|
||||
.version {
|
||||
font-size: 11px;
|
||||
color: #666;
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
import { Component, output } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
@Component({
|
||||
selector: 'app-start-menu',
|
||||
standalone: true,
|
||||
imports: [CommonModule],
|
||||
templateUrl: './start-menu.component.html',
|
||||
styleUrl: './start-menu.component.scss',
|
||||
})
|
||||
export class StartMenuComponent {
|
||||
readonly launchApp = output<string>();
|
||||
readonly close = output<void>();
|
||||
|
||||
apps = [
|
||||
{ id: 'file-explorer', icon: '📁', title: 'File Explorer', description: 'Browse files and folders' },
|
||||
{ id: 'terminal', icon: '⬛', title: 'Terminal', description: 'Command-line interface' },
|
||||
{ id: 'text-editor', icon: '📝', title: 'Text Editor', description: 'Edit text and code files' },
|
||||
{ id: 'browser', icon: '🌐', title: 'Web Browser', description: 'Browse the web' },
|
||||
{ id: 'settings', icon: '⚙️', title: 'Settings', description: 'System configuration' },
|
||||
{ id: 'remote-display', icon: '🖥️', title: 'Remote Desktop', description: 'Connect to a VM session' },
|
||||
];
|
||||
|
||||
onLaunch(id: string): void {
|
||||
this.launchApp.emit(id);
|
||||
this.close.emit();
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user