Make QOffice apps standalone Office-style surfaces
This commit is contained in:
+25
-98
@@ -1,23 +1,16 @@
|
||||
import {
|
||||
BarChart3,
|
||||
FileText,
|
||||
HelpCircle,
|
||||
Mail,
|
||||
MonitorCog,
|
||||
PanelRight,
|
||||
Presentation,
|
||||
ChevronDown,
|
||||
Redo2,
|
||||
Save,
|
||||
Search,
|
||||
Settings,
|
||||
Share2,
|
||||
Table2
|
||||
Undo2
|
||||
} from "lucide-react";
|
||||
import type { ReactNode } from "react";
|
||||
import type { AppId } from "../types";
|
||||
|
||||
interface AppShellProps {
|
||||
activeApp: AppId;
|
||||
onAppChange: (appId: AppId) => void;
|
||||
status: {
|
||||
savedLabel: string;
|
||||
updatedAt?: string;
|
||||
@@ -25,85 +18,37 @@ interface AppShellProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
const apps: Array<{ id: AppId; label: string; icon: typeof FileText; accent: string }> = [
|
||||
{ id: "word", label: "QWord", icon: FileText, accent: "word" },
|
||||
{ id: "sheets", label: "QExcell", icon: Table2, accent: "sheets" },
|
||||
{ id: "slides", label: "QPowerPoint", icon: Presentation, accent: "slides" },
|
||||
{ id: "mail", label: "QOutlook", icon: Mail, accent: "mail" }
|
||||
];
|
||||
|
||||
const ribbonGroups = [
|
||||
{
|
||||
title: "Буфер",
|
||||
commands: [
|
||||
{ label: "Вставить", icon: FileText },
|
||||
{ label: "Копировать", icon: Save }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "Вставка",
|
||||
commands: [
|
||||
{ label: "Таблица", icon: Table2 },
|
||||
{ label: "Диаграмма", icon: BarChart3 },
|
||||
{ label: "Панель", icon: PanelRight }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "Совместная работа",
|
||||
commands: [
|
||||
{ label: "Поделиться", icon: Share2 },
|
||||
{ label: "Параметры", icon: MonitorCog }
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
export function AppShell({ activeApp, onAppChange, status, children }: AppShellProps) {
|
||||
const active = apps.find((app) => app.id === activeApp) ?? apps[0];
|
||||
const appTitles: Record<AppId, string> = {
|
||||
word: "Документ1 - QWord",
|
||||
sheets: "Книга1 - QExcell",
|
||||
slides: "Презентация1 - QPowerPoint",
|
||||
mail: "Почта - QOutlook"
|
||||
};
|
||||
|
||||
export function AppShell({ activeApp, status, children }: AppShellProps) {
|
||||
return (
|
||||
<div className="qoffice-shell">
|
||||
<aside className="app-rail" aria-label="Приложения QOffice">
|
||||
<div className="brand">QOffice</div>
|
||||
<nav className="app-nav">
|
||||
{apps.map((app) => {
|
||||
const Icon = app.icon;
|
||||
return (
|
||||
<button
|
||||
key={app.id}
|
||||
className={`app-nav-item accent-${app.accent} ${activeApp === app.id ? "is-active" : ""}`}
|
||||
type="button"
|
||||
onClick={() => onAppChange(app.id)}
|
||||
aria-pressed={activeApp === app.id}
|
||||
title={`Открыть ${app.label}`}
|
||||
>
|
||||
<span className="app-icon">
|
||||
<Icon size={22} strokeWidth={2.2} />
|
||||
</span>
|
||||
<span>{app.label}</span>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</nav>
|
||||
<div className="rail-footer">
|
||||
<button className="icon-button" type="button" title="Параметры" aria-label="Параметры">
|
||||
<Settings size={18} />
|
||||
</button>
|
||||
<button className="icon-button" type="button" title="Справка" aria-label="Справка">
|
||||
<HelpCircle size={18} />
|
||||
</button>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<main className="workspace">
|
||||
<header className="titlebar">
|
||||
<div>
|
||||
<strong>{active.label}</strong>
|
||||
<span>{status.savedLabel}</span>
|
||||
<div className="quick-access" aria-label="Панель быстрого доступа">
|
||||
<button type="button" title="Сохранить" aria-label="Сохранить">
|
||||
<Save size={16} />
|
||||
</button>
|
||||
<button type="button" title="Отменить" aria-label="Отменить">
|
||||
<Undo2 size={16} />
|
||||
</button>
|
||||
<button type="button" title="Повторить" aria-label="Повторить">
|
||||
<Redo2 size={16} />
|
||||
</button>
|
||||
<button type="button" title="Настроить панель" aria-label="Настроить панель">
|
||||
<ChevronDown size={14} />
|
||||
</button>
|
||||
</div>
|
||||
<strong className="window-title">{appTitles[activeApp]}</strong>
|
||||
<div className="titlebar-actions">
|
||||
<label className="command-search">
|
||||
<Search size={16} />
|
||||
<input placeholder="Найти команду" aria-label="Найти команду" />
|
||||
<input placeholder="Поиск" aria-label="Поиск" />
|
||||
</label>
|
||||
<button className="primary-command" type="button">
|
||||
<Share2 size={16} />
|
||||
@@ -120,24 +65,6 @@ export function AppShell({ activeApp, onAppChange, status, children }: AppShellP
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<div className="ribbon-groups">
|
||||
{ribbonGroups.map((group) => (
|
||||
<div className="ribbon-group" key={group.title}>
|
||||
<div className="ribbon-command-row">
|
||||
{group.commands.map((command) => {
|
||||
const Icon = command.icon;
|
||||
return (
|
||||
<button type="button" key={command.label} className="tool-button" title={command.label}>
|
||||
<Icon size={17} />
|
||||
<span>{command.label}</span>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<span className="ribbon-group-title">{group.title}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div className="surface-host">{children}</div>
|
||||
|
||||
Reference in New Issue
Block a user