add custom titlebar
@@ -4,6 +4,7 @@ import { Container, Content, Footer } from 'rsuite';
|
||||
import classNames from 'classnames';
|
||||
import Sidebar from './Sidebar';
|
||||
import '../../styles/Layout.global.css';
|
||||
import Titlebar from './Titlebar';
|
||||
|
||||
const Layout = ({ footer, children }: any) => {
|
||||
const [expandSidebar, setExpandSidebar] = useState(true);
|
||||
@@ -50,6 +51,7 @@ const Layout = ({ footer, children }: any) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Titlebar />
|
||||
<Sidebar
|
||||
expand={expandSidebar}
|
||||
handleToggle={handleToggle}
|
||||
|
||||
53
src/components/layout/Titlebar.tsx
Normal file
@@ -0,0 +1,53 @@
|
||||
import React from 'react';
|
||||
import '../../styles/Titlebar.global.css';
|
||||
|
||||
const Titlebar = () => {
|
||||
return (
|
||||
<header id="titlebar">
|
||||
<div id="drag-region">
|
||||
<div id="window-title">
|
||||
<span>{document.title}</span>
|
||||
</div>
|
||||
<div id="window-controls">
|
||||
<div className="button" id="min-button">
|
||||
<img
|
||||
className="icon"
|
||||
srcSet="icons/min-w-10.png 1x, icons/min-w-12.png 1.25x, icons/min-w-15.png 1.5x, icons/min-w-15.png 1.75x, icons/min-w-20.png 2x, icons/min-w-20.png 2.25x, icons/min-w-24.png 2.5x, icons/min-w-30.png 3x, icons/min-w-30.png 3.5x"
|
||||
draggable="false"
|
||||
alt=""
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="button" id="max-button">
|
||||
<img
|
||||
className="icon"
|
||||
srcSet="icons/max-w-10.png 1x, icons/max-w-12.png 1.25x, icons/max-w-15.png 1.5x, icons/max-w-15.png 1.75x, icons/max-w-20.png 2x, icons/max-w-20.png 2.25x, icons/max-w-24.png 2.5x, icons/max-w-30.png 3x, icons/max-w-30.png 3.5x"
|
||||
draggable="false"
|
||||
alt=""
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="button" id="restore-button">
|
||||
<img
|
||||
className="icon"
|
||||
srcSet="icons/restore-w-10.png 1x, icons/restore-w-12.png 1.25x, icons/restore-w-15.png 1.5x, icons/restore-w-15.png 1.75x, icons/restore-w-20.png 2x, icons/restore-w-20.png 2.25x, icons/restore-w-24.png 2.5x, icons/restore-w-30.png 3x, icons/restore-w-30.png 3.5x"
|
||||
draggable="false"
|
||||
alt=""
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="button" id="close-button">
|
||||
<img
|
||||
className="icon"
|
||||
srcSet="icons/close-w-10.png 1x, icons/close-w-12.png 1.25x, icons/close-w-15.png 1.5x, icons/close-w-15.png 1.75x, icons/close-w-20.png 2x, icons/close-w-20.png 2.25x, icons/close-w-24.png 2.5x, icons/close-w-30.png 3x, icons/close-w-30.png 3.5x"
|
||||
draggable="false"
|
||||
alt=""
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
};
|
||||
|
||||
export default Titlebar;
|
||||
BIN
src/icons/close-k-10.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
src/icons/close-k-12.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
src/icons/close-k-15.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
src/icons/close-k-20.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
src/icons/close-k-24.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
src/icons/close-k-30.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
src/icons/close-w-10.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
src/icons/close-w-12.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
src/icons/close-w-15.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
src/icons/close-w-20.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
src/icons/close-w-24.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
src/icons/close-w-30.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
src/icons/max-k-10.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
src/icons/max-k-12.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
src/icons/max-k-15.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
src/icons/max-k-20.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
src/icons/max-k-24.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
src/icons/max-k-30.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
src/icons/max-w-10.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
src/icons/max-w-12.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
src/icons/max-w-15.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
src/icons/max-w-20.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
src/icons/max-w-24.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
src/icons/max-w-30.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
src/icons/min-k-10.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
src/icons/min-k-12.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
src/icons/min-k-15.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
src/icons/min-k-20.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
src/icons/min-k-24.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
src/icons/min-k-30.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
src/icons/min-w-10.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
src/icons/min-w-12.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
src/icons/min-w-15.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
src/icons/min-w-20.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
src/icons/min-w-24.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
src/icons/min-w-30.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
src/icons/restore-k-10.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
src/icons/restore-k-12.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
src/icons/restore-k-15.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
src/icons/restore-k-20.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
src/icons/restore-k-24.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
src/icons/restore-k-30.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
src/icons/restore-w-10.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
src/icons/restore-w-12.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
src/icons/restore-w-15.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
src/icons/restore-w-20.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
src/icons/restore-w-24.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
src/icons/restore-w-30.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
@@ -75,10 +75,12 @@ const createWindow = async () => {
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
enableRemoteModule: true,
|
||||
preload: path.join(__dirname, 'preload.ts'), // Add custom titlebar functionality
|
||||
},
|
||||
autoHideMenuBar: true,
|
||||
minWidth: 800,
|
||||
minHeight: 600,
|
||||
frame: false,
|
||||
});
|
||||
|
||||
mainWindow.loadURL(`file://${__dirname}/index.html`);
|
||||
|
||||
50
src/preload.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import { remote } from 'electron';
|
||||
|
||||
const win = remote.getCurrentWindow(); /* Note this is different to the
|
||||
html global `window` variable */
|
||||
|
||||
window.onbeforeunload = () => {
|
||||
/* If window is reloaded, remove win event listeners
|
||||
(DOM element listeners get auto garbage collected but not
|
||||
Electron win listeners as the win is not dereferenced unless closed) */
|
||||
win.removeAllListeners();
|
||||
};
|
||||
|
||||
function handleWindowControls() {
|
||||
// Make minimise/maximise/restore/close buttons work when they are clicked
|
||||
document.getElementById('min-button')?.addEventListener('click', () => {
|
||||
win.minimize();
|
||||
});
|
||||
|
||||
document.getElementById('max-button')?.addEventListener('click', () => {
|
||||
win.maximize();
|
||||
});
|
||||
|
||||
document.getElementById('restore-button')?.addEventListener('click', () => {
|
||||
win.unmaximize();
|
||||
});
|
||||
|
||||
document.getElementById('close-button')?.addEventListener('click', () => {
|
||||
win.close();
|
||||
});
|
||||
|
||||
function toggleMaxRestoreButtons() {
|
||||
if (win.isMaximized()) {
|
||||
document.body.classList.add('maximized');
|
||||
} else {
|
||||
document.body.classList.remove('maximized');
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle maximise/restore buttons when maximisation/unmaximisation occurs
|
||||
toggleMaxRestoreButtons();
|
||||
win.on('maximize', toggleMaxRestoreButtons);
|
||||
win.on('unmaximize', toggleMaxRestoreButtons);
|
||||
}
|
||||
|
||||
// When document has loaded, initialise
|
||||
document.onreadystatechange = () => {
|
||||
if (document.readyState === 'complete') {
|
||||
handleWindowControls();
|
||||
}
|
||||
};
|
||||
@@ -12,10 +12,19 @@ body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
border: 1px solid #48545c;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
#root {
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 30px !important;
|
||||
}
|
||||
|
||||
.rs-panel-heading {
|
||||
padding-bottom: 5px !important;
|
||||
}
|
||||
|
||||
@@ -23,5 +23,5 @@
|
||||
}
|
||||
|
||||
.container__page {
|
||||
height: 100vh;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
.container__sidebar {
|
||||
position: fixed;
|
||||
top: 33px; /* Account for custom titlebar */
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
|
||||
110
src/styles/Titlebar.global.css
Normal file
@@ -0,0 +1,110 @@
|
||||
@media (-webkit-device-pixel-ratio: 1.5),
|
||||
(device-pixel-ratio: 1.5),
|
||||
(-webkit-device-pixel-ratio: 2),
|
||||
(device-pixel-ratio: 2),
|
||||
(-webkit-device-pixel-ratio: 3),
|
||||
(device-pixel-ratio: 3) {
|
||||
#window-controls .icon {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
#main {
|
||||
height: calc(100% - 32px);
|
||||
margin-top: 32px;
|
||||
padding: 20px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
#titlebar {
|
||||
display: block;
|
||||
position: fixed;
|
||||
height: 32px;
|
||||
width: calc(100% - 2px); /*Compensate for body 1px border*/
|
||||
background: #1a1d24;
|
||||
padding: 4px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
#titlebar #drag-region {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
-webkit-app-region: drag;
|
||||
}
|
||||
|
||||
#window-controls {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 46px);
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#window-controls .button {
|
||||
grid-row: 1 / span 1;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#min-button {
|
||||
grid-column: 1;
|
||||
}
|
||||
|
||||
#max-button,
|
||||
#restore-button {
|
||||
grid-column: 2;
|
||||
}
|
||||
|
||||
#close-button {
|
||||
grid-column: 3;
|
||||
}
|
||||
|
||||
#window-controls {
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
|
||||
#window-controls .button {
|
||||
user-select: none;
|
||||
}
|
||||
#window-controls .button:hover {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
#window-controls .button:active {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
#close-button:hover {
|
||||
background: #e81123 !important;
|
||||
}
|
||||
#close-button:active {
|
||||
background: #f1707a !important;
|
||||
}
|
||||
#close-button:active .icon {
|
||||
filter: invert(1);
|
||||
}
|
||||
|
||||
#restore-button {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.maximized #titlebar {
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#window-title {
|
||||
margin-left: 12px;
|
||||
}
|
||||
|
||||
.maximized #restore-button {
|
||||
display: flex !important;
|
||||
}
|
||||
|
||||
.maximized #max-button {
|
||||
display: none;
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
@table-border-color: #0f131a;
|
||||
|
||||
// Sidenav
|
||||
@sidenav-default-bg: #0f131a;
|
||||
@sidenav-collapse-transition-config: 0s;
|
||||
|
||||
// Panel
|
||||
|
||||