Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 99 additions & 19 deletions src/app/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import 'react-splitter-layout/lib/index.css';
import './app.scss';
import { Button, Classes, Spinner } from '@blueprintjs/core';
import { Manual } from '@blueprintjs/icons';
import { Console, Manual } from '@blueprintjs/icons';
import React, { useEffect, useState } from 'react';

type SideView = 'off' | 'docs';
Expand Down Expand Up @@ -70,14 +70,81 @@ const Docs: React.FunctionComponent = () => {
);
};

/**
* HACK: react-splitter-layout is an uncontrolled component, so there is no
* clean way to reset its size programmatically.
*/
function resetSplitterSize(className: string, defaultSize: number): void {
const container = document.querySelector<HTMLElement>(className);

if (!container) {
return;
}

const fiberKey = Object.keys(container).find((k) => k.startsWith('__reactFiber'));
if (!fiberKey) {
return;
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
let fiber: any = (container as any)[fiberKey];
while (fiber && !fiber.stateNode?.handleMouseMove) {
fiber = fiber.return;
}

fiber?.stateNode?.setState({ secondaryPaneSize: defaultSize });
}

const App: React.FunctionComponent = () => {
const i18n = useI18n();
const { isDarkMode } = useTernaryDarkMode();
const [sideView, setSideView] = useState<SideView>('off');
const [isDragging, setIsDragging] = useState(false);

const [docsSplit, setDocsSplit] = useLocalStorage('app-docs-split', 30);
const [terminalSplit, setTerminalSplit] = useLocalStorage('app-terminal-split', 30);
const defaultDocsSplit = 30;
const [docsSplit, setDocsSplit] = useLocalStorage(
'app-docs-split',
defaultDocsSplit,
);

const resetDocsSplit = () => {
setDocsSplit(defaultDocsSplit);
resetSplitterSize(
'.splitter-layout.pb-show-docs, .splitter-layout.pb-hide-docs',
defaultDocsSplit,
);
};

const docsOnClick = () => {
if (sideView === 'docs' && docsSplit < 10) {
resetDocsSplit();
} else {
setSideView(sideView === 'docs' ? 'off' : 'docs');
}
};

const defaultTerminalSplit = 30;
const [terminalSplit, setTerminalSplit] = useLocalStorage(
'app-terminal-split',
defaultTerminalSplit,
);
const [terminalVisible, setTerminalVisible] = useState(true);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should use local storage so that it stays in the same state when the app is restarted.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Users often lose it and then wonder why motors don't work. I think it's fine to have it open by default, perhaps at a smaller default size.

Unlike before, there is now a one-click way to close it, so it's very easy to hide it again if you really want to.


const resetTerminalSplit = () => {
setTerminalSplit(defaultTerminalSplit);
resetSplitterSize(
'.splitter-layout.pb-show-terminal, .splitter-layout.pb-hide-terminal',
defaultTerminalSplit,
);
};

const terminalOnClick = () => {
if (terminalVisible && terminalSplit < 10) {
resetTerminalSplit();
} else {
setTerminalVisible(!terminalVisible);
}
};

// Classes.DARK has to be applied to body element, otherwise it won't
// affect portals
Expand Down Expand Up @@ -128,6 +195,11 @@ const App: React.FunctionComponent = () => {
>
<SplitterLayout
vertical={true}
customClassName={
terminalVisible
? 'pb-show-terminal'
: 'pb-hide-terminal'
}
percentage={true}
secondaryInitialSize={terminalSplit}
onSecondaryPaneSizeChange={setTerminalSplit}
Expand All @@ -142,22 +214,30 @@ const App: React.FunctionComponent = () => {
>
<Editor />
</React.Suspense>
<Button
className="pb-app-doc-button"
minimal
large
icon={<Manual />}
title={
sideView === 'docs'
? i18n.translate('docs.hide')
: i18n.translate('docs.show')
}
onClick={() =>
setSideView(
sideView === 'docs' ? 'off' : 'docs',
)
}
/>
<div className="pb-app-side-view-buttons">
<Button
large
intent="primary"
icon={<Console />}
title={
terminalVisible
? i18n.translate('terminal.hide')
: i18n.translate('terminal.show')
}
onClick={terminalOnClick}
/>
<Button
large
intent="primary"
icon={<Manual />}
title={
sideView === 'docs'
? i18n.translate('docs.hide')
: i18n.translate('docs.show')
}
onClick={docsOnClick}
/>
</div>
</main>
<aside
className="pb-app-terminal"
Expand Down
21 changes: 20 additions & 1 deletion src/app/app.scss
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,19 @@
}
}

.pb-app-doc-button {
.pb-app-side-view-buttons {
position: absolute;
bottom: bp.$pt-grid-size;
right: bp.$pt-grid-size;
z-index: bp.$pt-z-index-overlay;
display: flex;
flex-direction: row;
gap: bp.$pt-grid-size * 0.5;

// match toolbar button appearance: no box-shadow border or active-state shadow
& * {
box-shadow: unset !important;
}
}

$splitter-background-color: bp.$light-gray2;
Expand Down Expand Up @@ -203,3 +211,14 @@ div.pb-hide-docs > :not(.layout-pane-primary) {
width: 0 !important;
min-width: 0 !important;
}

// hide the terminal and resize separator
// Same approach as docs: keep the terminal in the DOM to preserve its content,
// but collapse the secondary pane to zero height.
div.pb-hide-terminal > :not(.layout-pane-primary) {
visibility: hidden;
pointer-events: none;
flex-basis: 0 !important;
height: 0 !important;
min-height: 0 !important;
}
4 changes: 4 additions & 0 deletions src/app/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,9 @@
"docs": {
"show": "Show documentation",
"hide": "Hide documentation"
},
"terminal": {
"show": "Show terminal",
"hide": "Hide terminal"
}
}