feat: panel-toggle-button
This commit is contained in:
@@ -181,6 +181,25 @@
|
|||||||
background-color: currentColor;
|
background-color: currentColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.window-titlebar-panel-icon {
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
border: 1.5px solid currentColor;
|
||||||
|
border-radius: 2px;
|
||||||
|
display: block;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-titlebar-panel-pane {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 33.3333%;
|
||||||
|
background-color: currentColor;
|
||||||
|
}
|
||||||
|
|
||||||
.window-titlebar-action-button:hover {
|
.window-titlebar-action-button:hover {
|
||||||
background-color: var(--vscode-toolbar-hoverBackground, rgba(90, 93, 94, 0.31));
|
background-color: var(--vscode-toolbar-hoverBackground, rgba(90, 93, 94, 0.31));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ type WindowControlsOverlayLike = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const WindowTitleBar: React.FC = () => {
|
export const WindowTitleBar: React.FC = () => {
|
||||||
const { sidebarVisible, toggleSidebar } = useAppStore();
|
const { sidebarVisible, panelVisible, toggleSidebar, togglePanel } = useAppStore();
|
||||||
const [windowTitle, setWindowTitle] = useState<string>(document.title || 'Blogging Desktop Server');
|
const [windowTitle, setWindowTitle] = useState<string>(document.title || 'Blogging Desktop Server');
|
||||||
const [openMenu, setOpenMenu] = useState<{ label: string; left: number } | null>(null);
|
const [openMenu, setOpenMenu] = useState<{ label: string; left: number } | null>(null);
|
||||||
const [showMnemonics, setShowMnemonics] = useState<boolean>(false);
|
const [showMnemonics, setShowMnemonics] = useState<boolean>(false);
|
||||||
@@ -416,6 +416,16 @@ export const WindowTitleBar: React.FC = () => {
|
|||||||
<span className="window-titlebar-sidebar-pane" data-shape="left-half" />
|
<span className="window-titlebar-sidebar-pane" data-shape="left-half" />
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
className="window-titlebar-action-button"
|
||||||
|
aria-label="Toggle Panel"
|
||||||
|
onClick={togglePanel}
|
||||||
|
title={`${panelVisible ? 'Hide' : 'Show'} Panel (Ctrl+J)`}
|
||||||
|
>
|
||||||
|
<span className="window-titlebar-panel-icon" data-shape="frame-square" aria-hidden="true">
|
||||||
|
<span className="window-titlebar-panel-pane" data-shape="bottom-half" />
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{openMenu && activeMenu && (
|
{openMenu && activeMenu && (
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ describe('WindowTitleBar', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
useAppStore.setState({
|
useAppStore.setState({
|
||||||
sidebarVisible: true,
|
sidebarVisible: true,
|
||||||
|
panelVisible: false,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -37,6 +38,42 @@ describe('WindowTitleBar', () => {
|
|||||||
expect(iconPane).toHaveAttribute('data-shape', 'left-half');
|
expect(iconPane).toHaveAttribute('data-shape', 'left-half');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('renders a right-side panel toggle button and toggles panel visibility', () => {
|
||||||
|
render(<WindowTitleBar />);
|
||||||
|
|
||||||
|
const toggleButton = screen.getByLabelText('Toggle Panel');
|
||||||
|
expect(toggleButton).toBeInTheDocument();
|
||||||
|
expect(toggleButton).toHaveAttribute('title', 'Show Panel (Ctrl+J)');
|
||||||
|
|
||||||
|
fireEvent.click(toggleButton);
|
||||||
|
|
||||||
|
expect(useAppStore.getState().panelVisible).toBe(true);
|
||||||
|
expect(toggleButton).toHaveAttribute('title', 'Hide Panel (Ctrl+J)');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('uses a VS Code-like panel toggle icon shape', () => {
|
||||||
|
render(<WindowTitleBar />);
|
||||||
|
|
||||||
|
const toggleButton = screen.getByLabelText('Toggle Panel');
|
||||||
|
const iconFrame = toggleButton.querySelector('.window-titlebar-panel-icon');
|
||||||
|
const iconPane = toggleButton.querySelector('.window-titlebar-panel-pane');
|
||||||
|
|
||||||
|
expect(iconFrame).not.toBeNull();
|
||||||
|
expect(iconPane).not.toBeNull();
|
||||||
|
expect(iconFrame).toHaveAttribute('data-shape', 'frame-square');
|
||||||
|
expect(iconPane).toHaveAttribute('data-shape', 'bottom-half');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('places panel toggle to the right of sidebar toggle', () => {
|
||||||
|
render(<WindowTitleBar />);
|
||||||
|
|
||||||
|
const actionButtons = Array.from(document.querySelectorAll('.window-titlebar-actions .window-titlebar-action-button'));
|
||||||
|
|
||||||
|
expect(actionButtons).toHaveLength(2);
|
||||||
|
expect(actionButtons[0]).toHaveAttribute('aria-label', 'Toggle Sidebar');
|
||||||
|
expect(actionButtons[1]).toHaveAttribute('aria-label', 'Toggle Panel');
|
||||||
|
});
|
||||||
|
|
||||||
it('updates overlay inset CSS variables when window controls geometry changes', () => {
|
it('updates overlay inset CSS variables when window controls geometry changes', () => {
|
||||||
const geometryListeners = new Set<EventListener>();
|
const geometryListeners = new Set<EventListener>();
|
||||||
let rect = {
|
let rect = {
|
||||||
|
|||||||
Reference in New Issue
Block a user