style(workspace): align UI styling with ListsView (rounded-full avatars, dark canvas borders, unified Radio live badge, unified Users share button)
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 56s
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 56s
This commit is contained in:
1 parent
cedf5aebff
commit
634195b629
2 files changed
+92
-65
No files matched your search
@@ -1,12 +1,11 @@
|
||||
import React, { useState, useEffect, useRef, useCallback, useMemo } from 'react';
|
||||
import {
|
||||
Pin,
|
||||
Share2,
|
||||
Users,
|
||||
Trash2,
|
||||
Download,
|
||||
Folder as FolderIcon,
|
||||
Wifi,
|
||||
WifiOff,
|
||||
Radio,
|
||||
Loader2,
|
||||
ArrowLeft,
|
||||
Hash,
|
||||
@@ -590,16 +589,16 @@ export const DocumentEditor: React.FC<DocumentEditorProps> = ({
|
||||
return (
|
||||
<div className="flex-1 flex flex-col h-full min-h-0 bg-canvas overflow-y-auto">
|
||||
{/* Top Document Action Bar */}
|
||||
<div className="sticky top-0 z-20 flex flex-wrap items-center justify-between gap-2 px-4 sm:px-6 py-2.5 bg-panel border-b border-border shadow-sm">
|
||||
<div className="sticky top-0 z-20 flex flex-wrap items-center justify-between gap-2 px-4 sm:px-6 py-2 bg-canvas border-b border-border">
|
||||
<div className="flex items-center gap-2 min-w-0">
|
||||
{onBackMobile && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onBackMobile}
|
||||
className="lg:hidden p-1.5 rounded-btn text-muted hover:text-main hover:bg-panel-elevated"
|
||||
className="lg:hidden btn-icon"
|
||||
title="Zurück zur Seitenleiste"
|
||||
>
|
||||
<ArrowLeft className="w-4 h-4" />
|
||||
<ArrowLeft className="w-3.5 h-3.5" />
|
||||
</button>
|
||||
)}
|
||||
|
||||
@@ -618,7 +617,7 @@ export const DocumentEditor: React.FC<DocumentEditorProps> = ({
|
||||
</button>
|
||||
|
||||
{showFolderMenu && canEdit && (
|
||||
<div className="absolute left-0 mt-1 w-52 panel-surface shadow-panel border border-border rounded-card py-1 z-30 max-h-60 overflow-y-auto">
|
||||
<div className="absolute left-0 mt-1 w-52 bg-canvas shadow-panel border border-border rounded-card py-1 z-30 max-h-60 overflow-y-auto">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleMoveToFolder('')}
|
||||
@@ -654,8 +653,8 @@ export const DocumentEditor: React.FC<DocumentEditorProps> = ({
|
||||
</div>
|
||||
|
||||
{/* Right Status & Action Buttons */}
|
||||
<div className="flex items-center gap-1">
|
||||
{/* Active Presence Avatars */}
|
||||
<div className="flex items-center gap-1.5">
|
||||
{/* Active Presence Avatars (Immer rund: rounded-full mit Überlappung) */}
|
||||
{activeUsers.length > 0 && (
|
||||
<div className="flex items-center -space-x-1.5 mr-1" title="Aktive Bearbeiter im Dokument">
|
||||
{activeUsers.map((u) => {
|
||||
@@ -668,13 +667,13 @@ export const DocumentEditor: React.FC<DocumentEditorProps> = ({
|
||||
return (
|
||||
<div key={u.clientId} className="relative group/avatar cursor-default">
|
||||
<div
|
||||
className="w-5 h-5 rounded-btn flex items-center justify-center text-[9px] font-bold text-white shadow-sm ring-1 ring-panel"
|
||||
className="w-6 h-6 rounded-full flex items-center justify-center text-[10px] font-bold text-white shadow-sm ring-2 ring-canvas"
|
||||
style={{ backgroundColor: u.userColor }}
|
||||
>
|
||||
{initials}
|
||||
</div>
|
||||
{/* Tooltip */}
|
||||
<div className="pointer-events-none absolute bottom-full mb-1 left-1/2 -translate-x-1/2 opacity-0 group-hover/avatar:opacity-100 transition-opacity whitespace-nowrap bg-panel-elevated text-main text-[10px] px-1.5 py-0.5 rounded-btn border border-border shadow-md z-30">
|
||||
<div className="pointer-events-none absolute bottom-full mb-1 left-1/2 -translate-x-1/2 opacity-0 group-hover/avatar:opacity-100 transition-opacity whitespace-nowrap bg-canvas text-main text-[10px] px-1.5 py-0.5 rounded-btn border border-border shadow-md z-30">
|
||||
{u.userName}
|
||||
</div>
|
||||
</div>
|
||||
@@ -683,80 +682,109 @@ export const DocumentEditor: React.FC<DocumentEditorProps> = ({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Sync Status Badge */}
|
||||
<span
|
||||
className={`inline-flex items-center gap-1 px-2 py-0.5 rounded-btn text-[10px] font-mono font-medium ${
|
||||
{/* Sharing Section (1:1 styling from SharedListsView) */}
|
||||
{doc && (() => {
|
||||
const isOwner = doc.isOwner !== false && (doc.userId === currentUser?.id || !doc.userId || currentUser?.role === 'admin');
|
||||
const sharesCount = doc.sharedWith?.length || 0;
|
||||
const isShared = sharesCount > 0 || !isOwner || Boolean(doc.sharedFolderId);
|
||||
const ownerDisplayName = doc.ownerName || 'Eigentümer';
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-1.5">
|
||||
{isShared && (
|
||||
isOwner ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onOpenShareModal(doc)}
|
||||
className="px-2 py-0.5 rounded text-[10px] font-semibold bg-accent/15 text-accent border border-accent/30 flex items-center gap-1 hover:bg-accent/25 hover:border-accent/50 transition-colors cursor-pointer"
|
||||
title="Teilen-Menü öffnen: Geteilt mit Nutzern/Gruppen anzeigen & verwalten"
|
||||
>
|
||||
<Users className="w-2.5 h-2.5" />
|
||||
<span>Geteilt{sharesCount > 0 ? ` (${sharesCount})` : ''}</span>
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onOpenShareModal(doc)}
|
||||
className="px-2 py-0.5 rounded text-[10px] font-medium border border-border text-muted flex items-center gap-1 bg-surface-subtle hover:bg-panel-elevated hover:text-main transition-colors cursor-pointer"
|
||||
title="Teilen & Berechtigungen öffnen"
|
||||
>
|
||||
<Users className="w-2.5 h-2.5" />
|
||||
<span>Geteilt von {ownerDisplayName}</span>
|
||||
</button>
|
||||
)
|
||||
)}
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onOpenShareModal(doc)}
|
||||
className="btn-icon"
|
||||
title="Notiz teilen und Berechtigungen"
|
||||
>
|
||||
<Users className="w-3.5 h-3.5" />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
})()}
|
||||
|
||||
{/* Live Sync Status (1:1 styling from SharedListsView) */}
|
||||
<div
|
||||
className={`h-8 w-8 flex items-center justify-center rounded-btn border transition-colors ${
|
||||
syncStatus === 'connected'
|
||||
? 'bg-status-ok/10 text-status-ok border border-status-ok/30'
|
||||
? 'bg-emerald-500/10 border-emerald-500/30 text-emerald-500'
|
||||
: syncStatus === 'connecting' || syncStatus === 'reconnecting'
|
||||
? 'bg-status-warn/10 text-status-warn border border-status-warn/30'
|
||||
: 'bg-panel-elevated text-muted border border-border'
|
||||
? 'bg-amber-500/10 border-amber-500/30 text-amber-500'
|
||||
: 'bg-canvas border-border text-muted'
|
||||
}`}
|
||||
title={
|
||||
syncStatus === 'connected'
|
||||
? 'Echtzeit-Synchronisation aktiv'
|
||||
: 'Verbindung wird hergestellt…'
|
||||
? 'Live synchronisiert (WebSocket online)'
|
||||
: syncStatus === 'connecting' || syncStatus === 'reconnecting'
|
||||
? 'Verbindung wird aufgebaut…'
|
||||
: 'Offline'
|
||||
}
|
||||
>
|
||||
{syncStatus === 'connected' ? (
|
||||
<>
|
||||
<Wifi className="w-2.5 h-2.5" />
|
||||
<span className="hidden sm:inline">LIVE</span>
|
||||
</>
|
||||
) : syncStatus === 'connecting' || syncStatus === 'reconnecting' ? (
|
||||
<>
|
||||
<Loader2 className="w-2.5 h-2.5 animate-spin" />
|
||||
<span className="hidden sm:inline">SYNC…</span>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<WifiOff className="w-2.5 h-2.5" />
|
||||
<span className="hidden sm:inline">OFFLINE</span>
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
<Radio
|
||||
className={`w-3.5 h-3.5 ${
|
||||
syncStatus === 'connected'
|
||||
? 'animate-pulse'
|
||||
: syncStatus === 'connecting' || syncStatus === 'reconnecting'
|
||||
? 'animate-spin'
|
||||
: ''
|
||||
}`}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleTogglePin}
|
||||
className={`p-1.5 rounded-btn transition-colors ${
|
||||
className={`btn-icon ${
|
||||
doc?.isPinned
|
||||
? 'text-status-warn bg-status-warn/10'
|
||||
: 'text-muted hover:text-main hover:bg-panel-elevated'
|
||||
? 'text-status-warn bg-status-warn/10 border-status-warn/30'
|
||||
: ''
|
||||
}`}
|
||||
title={doc?.isPinned ? 'Anheftung lösen' : 'Notiz anheften'}
|
||||
>
|
||||
<Pin className="w-4 h-4" />
|
||||
<Pin className="w-3.5 h-3.5" />
|
||||
</button>
|
||||
|
||||
{doc && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onOpenShareModal(doc)}
|
||||
className="p-1.5 rounded-btn text-muted hover:text-accent hover:bg-panel-elevated transition-colors"
|
||||
title="Notiz teilen / Berechtigungen"
|
||||
>
|
||||
<Share2 className="w-4 h-4" />
|
||||
</button>
|
||||
)}
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleExportMarkdown}
|
||||
className="p-1.5 rounded-btn text-muted hover:text-main hover:bg-panel-elevated transition-colors"
|
||||
className="btn-icon"
|
||||
title="Als Markdown (.md) exportieren"
|
||||
>
|
||||
<Download className="w-4 h-4" />
|
||||
<Download className="w-3.5 h-3.5" />
|
||||
</button>
|
||||
|
||||
{canEdit && (doc?.isOwner || doc?.permission === 'owner') && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onDeleteDoc(docId)}
|
||||
className="p-1.5 rounded-btn text-muted hover:text-status-err hover:bg-status-err/10 transition-colors"
|
||||
className="btn-icon-danger"
|
||||
title="Notiz löschen"
|
||||
>
|
||||
<Trash2 className="w-4 h-4" />
|
||||
<Trash2 className="w-3.5 h-3.5" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
@@ -773,7 +801,7 @@ export const DocumentEditor: React.FC<DocumentEditorProps> = ({
|
||||
{breadcrumbs.length > 0 && (
|
||||
<nav
|
||||
aria-label="Breadcrumb Navigation"
|
||||
className="flex items-center flex-wrap gap-1.5 py-1.5 px-3 bg-panel-elevated/70 border border-border rounded-card text-xs text-muted"
|
||||
className="flex items-center flex-wrap gap-1.5 py-1.5 px-3 bg-canvas border border-border rounded-btn text-xs text-muted"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
@@ -918,7 +946,7 @@ export const DocumentEditor: React.FC<DocumentEditorProps> = ({
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => canEdit && setShowEmojiPicker((prev) => !prev)}
|
||||
className="w-10 h-10 rounded-btn bg-panel-elevated border border-border hover:border-strong flex items-center justify-center text-xl transition-colors"
|
||||
className="w-10 h-10 rounded-btn bg-canvas border border-border hover:border-strong flex items-center justify-center text-xl transition-colors"
|
||||
title="Icon ändern"
|
||||
>
|
||||
{doc?.icon || '📝'}
|
||||
|
||||
@@ -15,7 +15,6 @@ import {
|
||||
Users,
|
||||
Plus,
|
||||
X,
|
||||
Share2,
|
||||
ExternalLink,
|
||||
Sparkles,
|
||||
} from 'lucide-react';
|
||||
@@ -514,12 +513,12 @@ export const WorkspaceView: React.FC<WorkspaceViewProps> = ({
|
||||
}, [activeDocShares, shareModalDoc, shareModalFolder]);
|
||||
|
||||
return (
|
||||
<div className="flex h-[calc(100vh-4rem)] panel-surface rounded-card border border-border overflow-hidden shadow-panel">
|
||||
<div className="flex h-[calc(100vh-4rem)] bg-canvas rounded-card border border-border overflow-hidden shadow-panel">
|
||||
{/* LEFT MASTER SIDEBAR */}
|
||||
<aside
|
||||
className={`${
|
||||
mobileShowDetail ? 'hidden lg:flex' : 'flex'
|
||||
} w-full lg:w-72 xl:w-80 shrink-0 flex-col bg-panel border-r border-border`}
|
||||
} w-full lg:w-72 xl:w-80 shrink-0 flex-col bg-canvas border-r border-border`}
|
||||
>
|
||||
{/* Sidebar Header & Actions */}
|
||||
<div className="p-3 border-b border-border space-y-2">
|
||||
@@ -733,9 +732,9 @@ export const WorkspaceView: React.FC<WorkspaceViewProps> = ({
|
||||
type="button"
|
||||
onClick={() => openFolderShareModal(sharedFolderObj)}
|
||||
className="p-1 rounded-btn hover:bg-canvas text-accent"
|
||||
title="Ordner-Freigabe"
|
||||
title="Ordner teilen und Berechtigungen"
|
||||
>
|
||||
<Share2 className="w-3 h-3" />
|
||||
<Users className="w-3 h-3" />
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
@@ -916,7 +915,7 @@ export const WorkspaceView: React.FC<WorkspaceViewProps> = ({
|
||||
{tagDetails?.documents.map((docGroup) => (
|
||||
<div
|
||||
key={docGroup.docId}
|
||||
className="p-3 rounded-card border border-border bg-panel space-y-1.5"
|
||||
className="p-3 rounded-card border border-border bg-canvas space-y-1.5"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
@@ -1001,10 +1000,10 @@ export const WorkspaceView: React.FC<WorkspaceViewProps> = ({
|
||||
return (
|
||||
<div
|
||||
key={folderName}
|
||||
className="border border-border rounded-card bg-panel overflow-hidden"
|
||||
className="border border-border rounded-card bg-canvas overflow-hidden"
|
||||
>
|
||||
{/* Folder Header */}
|
||||
<div className="p-2 bg-panel-elevated border-b border-border flex items-center justify-between">
|
||||
<div className="p-2 bg-canvas border-b border-border flex items-center justify-between">
|
||||
<div
|
||||
onClick={() => {
|
||||
setCollapsedTodoFolders((prev) => {
|
||||
|
||||
Reference in new issue
Block a user