feat(editor): account block fuses into the explorer footer
The floating corner box sat ON TOP of the file tree — create enough files and the last rows scrolled underneath it. The account + language block is now the explorer panel's footer (VS Code style): the tree scrolls above it, always. When the explorer is collapsed the block falls back to the small fixed corner box, so account and language stay reachable. The standalone language globe becomes a logged-out affordance: once the pro auth slot renders its signed-in marker (data-auth-user), a :has() rule hides the globe — signed-in users change language inside the account menu, where the pro overlay now hosts it.
This commit is contained in:
parent
57d7c93c90
commit
3b7abfc4f7
|
|
@ -615,3 +615,25 @@ body {
|
||||||
.app-header--with-toolbar .header-editor-toolbar .unified-toolbar {
|
.app-header--with-toolbar .header-editor-toolbar .unified-toolbar {
|
||||||
padding-right: 6px !important;
|
padding-right: 6px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ── Explorer account footer ─────────────────────────────────────────────
|
||||||
|
The account + language block fused to the bottom of the file explorer:
|
||||||
|
a long tree scrolls above it instead of hiding under a floating box.
|
||||||
|
(.editor-corner-box remains as the fallback when the explorer is
|
||||||
|
collapsed.) The standalone globe is a logged-out affordance only — once
|
||||||
|
the pro auth slot renders its signed-in marker, language moves inside
|
||||||
|
the account menu and the globe hides. */
|
||||||
|
.explorer-account-footer {
|
||||||
|
flex-shrink: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
padding: 6px 8px;
|
||||||
|
border-top: 1px solid #2f3542;
|
||||||
|
background: #171a21;
|
||||||
|
}
|
||||||
|
|
||||||
|
.explorer-account-footer:has([data-auth-user]) .corner-lang,
|
||||||
|
.editor-corner-box:has([data-auth-user]) .corner-lang {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { createPortal } from 'react-dom';
|
|
||||||
import { Link, useLocation } from 'react-router-dom';
|
import { Link, useLocation } from 'react-router-dom';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useProjectStore } from '../../store/useProjectStore';
|
import { useProjectStore } from '../../store/useProjectStore';
|
||||||
|
|
@ -309,20 +308,9 @@ export const AppHeader: React.FC<AppHeaderProps> = ({ autoSave, editorMenu, edit
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Editor corner box — bottom-left, IDE style: the account button
|
{/* The account + language block lives in the file-explorer footer now
|
||||||
(pro injects it into the auth slot) and the language switcher.
|
(EditorPage renders it) — fused so a long file tree never scrolls
|
||||||
Portaled to <body>: the header's backdrop-filter makes it a
|
underneath a floating box. */}
|
||||||
containing block, so a fixed child positioned inside it lands at
|
|
||||||
the header's bottom (the top of the page) instead of the
|
|
||||||
viewport's — measured at y=-4, on top of the logo. */}
|
|
||||||
{editorToolbar &&
|
|
||||||
createPortal(
|
|
||||||
<div className="editor-corner-box">
|
|
||||||
<LanguageSwitcher />
|
|
||||||
<div data-velxio-slot="header-auth" style={{ display: 'contents' }} />
|
|
||||||
</div>,
|
|
||||||
document.body,
|
|
||||||
)}
|
|
||||||
|
|
||||||
{showShareModal && <ShareModal onClose={() => setShowShareModal(false)} />}
|
{showShareModal && <ShareModal onClose={() => setShowShareModal(false)} />}
|
||||||
</header>
|
</header>
|
||||||
|
|
|
||||||
|
|
@ -84,3 +84,10 @@
|
||||||
bottom: 110%;
|
bottom: 110%;
|
||||||
margin: 0 0 6px;
|
margin: 0 0 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Same drop-up need at the explorer footer. */
|
||||||
|
.explorer-account-footer .velxio-language-menu {
|
||||||
|
top: auto;
|
||||||
|
bottom: 110%;
|
||||||
|
margin: 0 0 6px;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ import { showConfirmDialog } from '../store/useMessageDialogStore';
|
||||||
import { useAutoSaveProject } from '../hooks/useAutoSaveProject';
|
import { useAutoSaveProject } from '../hooks/useAutoSaveProject';
|
||||||
import { registerEditorCommand } from '../lib/editorCommands';
|
import { registerEditorCommand } from '../lib/editorCommands';
|
||||||
import { EditorMenuBar } from '../components/editor/EditorMenuBar';
|
import { EditorMenuBar } from '../components/editor/EditorMenuBar';
|
||||||
|
import { LanguageSwitcher } from '../components/layout/LanguageSwitcher';
|
||||||
import type { CompilationLog } from '../utils/compilationLogger';
|
import type { CompilationLog } from '../utils/compilationLogger';
|
||||||
import '../App.css';
|
import '../App.css';
|
||||||
|
|
||||||
|
|
@ -385,6 +386,22 @@ export const EditorPage: React.FC = () => {
|
||||||
there used to be 44+38. On narrow widths the strip wraps internally
|
there used to be 44+38. On narrow widths the strip wraps internally
|
||||||
and the header grows; the docked AI chat is avoided by the same
|
and the header grows; the docked AI chat is avoided by the same
|
||||||
padding-right the strip always had. */
|
padding-right the strip always had. */
|
||||||
|
/* Account + language block. Fused as the explorer panel's footer so a
|
||||||
|
long file tree scrolls ABOVE it instead of disappearing underneath a
|
||||||
|
floating box; when the explorer is collapsed it falls back to the
|
||||||
|
small fixed corner box. The standalone language switcher only shows
|
||||||
|
while logged OUT — signed-in users change language inside the account
|
||||||
|
menu (see pro HeaderAuth), and a CSS :has() rule hides the globe when
|
||||||
|
the auth slot renders a logged-in marker. */
|
||||||
|
const accountBlock = !isMobile ? (
|
||||||
|
<>
|
||||||
|
<span className="corner-lang">
|
||||||
|
<LanguageSwitcher />
|
||||||
|
</span>
|
||||||
|
<div data-velxio-slot="header-auth" style={{ display: 'contents' }} />
|
||||||
|
</>
|
||||||
|
) : undefined;
|
||||||
|
|
||||||
const unifiedToolbar = !isMobile ? (
|
const unifiedToolbar = !isMobile ? (
|
||||||
<div className="unified-toolbar">
|
<div className="unified-toolbar">
|
||||||
<button
|
<button
|
||||||
|
|
@ -558,10 +575,21 @@ export const EditorPage: React.FC = () => {
|
||||||
{explorerOpen && (
|
{explorerOpen && (
|
||||||
<>
|
<>
|
||||||
<div
|
<div
|
||||||
style={{ width: explorerWidth, flexShrink: 0, display: 'flex', overflow: 'hidden' }}
|
style={{
|
||||||
|
width: explorerWidth,
|
||||||
|
flexShrink: 0,
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
overflow: 'hidden',
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
|
<div style={{ flex: 1, minHeight: 0, display: 'flex', overflow: 'hidden' }}>
|
||||||
<FileExplorer onSaveClick={handleSaveClick} onNewClick={handleNewClick} />
|
<FileExplorer onSaveClick={handleSaveClick} onNewClick={handleNewClick} />
|
||||||
</div>
|
</div>
|
||||||
|
{accountBlock && (
|
||||||
|
<div className="explorer-account-footer">{accountBlock}</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
{!isMobile && (
|
{!isMobile && (
|
||||||
<div
|
<div
|
||||||
className="explorer-resize-handle"
|
className="explorer-resize-handle"
|
||||||
|
|
@ -571,6 +599,10 @@ export const EditorPage: React.FC = () => {
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{!explorerOpen && accountBlock && (
|
||||||
|
<div className="editor-corner-box">{accountBlock}</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Editor main area */}
|
{/* Editor main area */}
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue