Skip to content

Commit 54c619e

Browse files
committed
Some mobile improvements for the UI; make console fill space better
1 parent faff263 commit 54c619e

File tree

9 files changed

+127
-93
lines changed

9 files changed

+127
-93
lines changed

resources/scripts/components/elements/Spinner.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React, { Suspense } from 'react';
22
import styled, { css, keyframes } from 'styled-components/macro';
33
import tw from 'twin.macro';
4+
import ErrorBoundary from '@/components/elements/ErrorBoundary';
45

56
export type SpinnerSize = 'small' | 'base' | 'large';
67

@@ -25,12 +26,12 @@ const SpinnerComponent = styled.div<Props>`
2526
border-width: 3px;
2627
border-radius: 50%;
2728
animation: ${spin} 1s cubic-bezier(0.55, 0.25, 0.25, 0.70) infinite;
28-
29+
2930
${props => props.size === 'small' ? tw`w-4 h-4 border-2` : (props.size === 'large' ? css`
3031
${tw`w-16 h-16`};
3132
border-width: 6px;
3233
` : null)};
33-
34+
3435
border-color: ${props => !props.isBlue ? 'rgba(255, 255, 255, 0.2)' : 'hsla(212, 92%, 43%, 0.2)'};
3536
border-top-color: ${props => !props.isBlue ? 'rgb(255, 255, 255)' : 'hsl(212, 92%, 43%)'};
3637
`;
@@ -58,7 +59,9 @@ Spinner.Size = {
5859

5960
Spinner.Suspense = ({ children, centered = true, size = Spinner.Size.LARGE, ...props }) => (
6061
<Suspense fallback={<Spinner centered={centered} size={size} {...props}/>}>
61-
{children}
62+
<ErrorBoundary>
63+
{children}
64+
</ErrorBoundary>
6265
</Suspense>
6366
);
6467
Spinner.Suspense.displayName = 'Spinner.Suspense';

resources/scripts/components/server/Console.tsx renamed to resources/scripts/components/server/console/Console.tsx

Lines changed: 22 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,22 @@
11
import React, { useEffect, useMemo, useRef, useState } from 'react';
2-
import { Terminal, ITerminalOptions } from 'xterm';
2+
import { ITerminalOptions, Terminal } from 'xterm';
33
import { FitAddon } from 'xterm-addon-fit';
44
import { SearchAddon } from 'xterm-addon-search';
55
import { SearchBarAddon } from 'xterm-addon-search-bar';
66
import { WebLinksAddon } from 'xterm-addon-web-links';
77
import { ScrollDownHelperAddon } from '@/plugins/XtermScrollDownHelperAddon';
88
import SpinnerOverlay from '@/components/elements/SpinnerOverlay';
99
import { ServerContext } from '@/state/server';
10-
import styled from 'styled-components/macro';
1110
import { usePermissions } from '@/plugins/usePermissions';
12-
import tw, { theme as th } from 'twin.macro';
11+
import { theme as th } from 'twin.macro';
1312
import 'xterm/css/xterm.css';
1413
import useEventListener from '@/plugins/useEventListener';
1514
import { debounce } from 'debounce';
1615
import { usePersistedState } from '@/plugins/usePersistedState';
1716
import { SocketEvent, SocketRequest } from '@/components/server/events';
17+
import classNames from 'classnames';
18+
import styles from './style.module.css';
19+
import { ChevronDoubleRightIcon } from '@heroicons/react/solid';
1820

1921
const theme = {
2022
background: th`colors.black`.toString(),
@@ -48,23 +50,6 @@ const terminalProps: ITerminalOptions = {
4850
theme: theme,
4951
};
5052

51-
const TerminalDiv = styled.div`
52-
&::-webkit-scrollbar {
53-
width: 8px;
54-
}
55-
56-
&::-webkit-scrollbar-thumb {
57-
${tw`bg-neutral-900`};
58-
}
59-
`;
60-
61-
const CommandInput = styled.input`
62-
${tw`text-sm transition-colors duration-150 px-2 bg-transparent border-0 border-b-2 border-transparent text-neutral-100 p-2 pl-0 w-full focus:ring-0`}
63-
&:focus {
64-
${tw`border-cyan-700`};
65-
}
66-
`;
67-
6853
export default () => {
6954
const TERMINAL_PRELUDE = '\u001b[1m\u001b[33mcontainer@pterodactyl~ \u001b[0m';
7055
const ref = useRef<HTMLDivElement>(null);
@@ -202,30 +187,25 @@ export default () => {
202187
}, [ connected, instance ]);
203188

204189
return (
205-
<div css={tw`text-xs font-mono relative`}>
206-
<SpinnerOverlay visible={!connected} size={'large'} />
207-
<div
208-
css={[
209-
tw`rounded-t p-2 bg-black w-full`,
210-
!canSendCommands && tw`rounded-b`,
211-
]}
212-
style={{ minHeight: '16rem' }}
213-
>
214-
<TerminalDiv id={'terminal'} ref={ref} />
190+
<div className={styles.terminal}>
191+
<SpinnerOverlay visible={!connected} size={'large'}/>
192+
<div className={classNames(styles.container, styles.overflows_container, { 'rounded-b': !canSendCommands })}>
193+
<div id={styles.terminal} ref={ref}/>
215194
</div>
216195
{canSendCommands &&
217-
<div css={tw`rounded-b bg-neutral-900 text-neutral-100 flex items-baseline`}>
218-
<div css={tw`flex-shrink-0 p-2 font-bold`}>$</div>
219-
<div css={tw`w-full`}>
220-
<CommandInput
221-
type={'text'}
222-
placeholder={'Type a command...'}
223-
aria-label={'Console command input.'}
224-
disabled={!instance || !connected}
225-
onKeyDown={handleCommandKeyDown}
226-
autoCorrect={'off'}
227-
autoCapitalize={'none'}
228-
/>
196+
<div className={classNames('relative', styles.overflows_container)}>
197+
<input
198+
className={classNames('peer', styles.command_input)}
199+
type={'text'}
200+
placeholder={'Type a command...'}
201+
aria-label={'Console command input.'}
202+
disabled={!instance || !connected}
203+
onKeyDown={handleCommandKeyDown}
204+
autoCorrect={'off'}
205+
autoCapitalize={'none'}
206+
/>
207+
<div className={classNames('text-gray-100 peer-focus:text-gray-50 peer-focus:animate-pulse', styles.command_icon)}>
208+
<ChevronDoubleRightIcon className={'w-4 h-4'}/>
229209
</div>
230210
</div>
231211
}

resources/scripts/components/server/console/PowerButtons.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import React, { useState } from 'react';
22
import { Button } from '@/components/elements/button/index';
33
import Can from '@/components/elements/Can';
44
import { ServerContext } from '@/state/server';
5-
import { PowerAction } from '@/components/server/ServerConsole';
5+
import { PowerAction } from '@/components/server/console/ServerConsoleContainer';
66
import { Dialog } from '@/components/elements/dialog';
77

88
interface PowerButtonProps {
@@ -41,7 +41,7 @@ export default ({ className }: PowerButtonProps) => {
4141
</Dialog.Confirm>
4242
<Can action={'control.start'}>
4343
<Button
44-
className={'w-24'}
44+
className={'w-full sm:w-24'}
4545
disabled={status !== 'offline'}
4646
onClick={onButtonClick.bind(this, 'start')}
4747
>
@@ -50,7 +50,7 @@ export default ({ className }: PowerButtonProps) => {
5050
</Can>
5151
<Can action={'control.restart'}>
5252
<Button.Text
53-
className={'w-24'}
53+
className={'w-full sm:w-24'}
5454
variant={Button.Variants.Secondary}
5555
disabled={!status}
5656
onClick={onButtonClick.bind(this, 'restart')}
@@ -60,7 +60,7 @@ export default ({ className }: PowerButtonProps) => {
6060
</Can>
6161
<Can action={'control.stop'}>
6262
<Button.Danger
63-
className={'w-24'}
63+
className={'w-full sm:w-24'}
6464
variant={killable ? undefined : Button.Variants.Secondary}
6565
disabled={status === 'offline'}
6666
onClick={onButtonClick.bind(this, killable ? 'kill' : 'stop')}

resources/scripts/components/server/ServerConsole.tsx renamed to resources/scripts/components/server/console/ServerConsoleContainer.tsx

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,38 +5,42 @@ import ContentContainer from '@/components/elements/ContentContainer';
55
import tw from 'twin.macro';
66
import ServerContentBlock from '@/components/elements/ServerContentBlock';
77
import isEqual from 'react-fast-compare';
8-
import ErrorBoundary from '@/components/elements/ErrorBoundary';
98
import Spinner from '@/components/elements/Spinner';
109
import Features from '@feature/Features';
11-
import Console from '@/components/server/Console';
12-
import StatGraphs from '@/components/server/StatGraphs';
10+
import Console from '@/components/server/console/Console';
11+
import StatGraphs from '@/components/server/console/StatGraphs';
1312
import PowerButtons from '@/components/server/console/PowerButtons';
14-
import ServerDetailsBlock from '@/components/server/ServerDetailsBlock';
13+
import ServerDetailsBlock from '@/components/server/console/ServerDetailsBlock';
1514

1615
export type PowerAction = 'start' | 'stop' | 'restart' | 'kill';
1716

18-
const ServerConsole = () => {
17+
const ServerConsoleContainer = () => {
1918
const name = ServerContext.useStoreState(state => state.server.data!.name);
2019
const description = ServerContext.useStoreState(state => state.server.data!.description);
2120
const isInstalling = ServerContext.useStoreState(state => state.server.data!.isInstalling);
2221
const isTransferring = ServerContext.useStoreState(state => state.server.data!.isTransferring);
2322
const eggFeatures = ServerContext.useStoreState(state => state.server.data!.eggFeatures, isEqual);
2423

2524
return (
26-
<ServerContentBlock title={'Console'} className={'grid grid-cols-4 gap-4'}>
27-
<div className={'flex space-x-4 items-end col-span-4'}>
28-
<div className={'flex-1'}>
25+
<ServerContentBlock title={'Console'} className={'flex flex-col gap-2 sm:gap-4'}>
26+
<div className={'flex gap-4 items-end'}>
27+
<div className={'hidden sm:block flex-1'}>
2928
<h1 className={'font-header text-2xl text-gray-50 leading-relaxed line-clamp-1'}>{name}</h1>
3029
<p className={'text-sm line-clamp-2'}>{description}</p>
3130
</div>
3231
<div className={'flex-1'}>
3332
<Can action={[ 'control.start', 'control.stop', 'control.restart' ]} matchAny>
34-
<PowerButtons className={'flex justify-end space-x-2'}/>
33+
<PowerButtons className={'flex sm:justify-end space-x-2'}/>
3534
</Can>
3635
</div>
3736
</div>
38-
<div className={'col-span-4 lg:col-span-1'}>
39-
<ServerDetailsBlock className={'flex flex-col space-y-4'}/>
37+
<div className={'grid grid-cols-4 gap-2 sm:gap-4'}>
38+
<ServerDetailsBlock className={'col-span-4 lg:col-span-1 order-last lg:order-none'}/>
39+
<div className={'col-span-4 lg:col-span-3'}>
40+
<Spinner.Suspense>
41+
<Console/>
42+
</Spinner.Suspense>
43+
</div>
4044
{isInstalling ?
4145
<div css={tw`mt-4 rounded bg-yellow-500 p-3`}>
4246
<ContentContainer>
@@ -60,17 +64,14 @@ const ServerConsole = () => {
6064
null
6165
}
6266
</div>
63-
<div className={'col-span-3'}>
67+
<div className={'grid grid-cols-1 md:grid-cols-3 gap-2 sm:gap-4'}>
6468
<Spinner.Suspense>
65-
<ErrorBoundary>
66-
<Console/>
67-
</ErrorBoundary>
6869
<StatGraphs/>
6970
</Spinner.Suspense>
70-
<Features enabled={eggFeatures}/>
7171
</div>
72+
<Features enabled={eggFeatures}/>
7273
</ServerContentBlock>
7374
);
7475
};
7576

76-
export default memo(ServerConsole, isEqual);
77+
export default memo(ServerConsoleContainer, isEqual);

resources/scripts/components/server/ServerDetailsBlock.tsx renamed to resources/scripts/components/server/console/ServerDetailsBlock.tsx

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,10 @@ import { SocketEvent, SocketRequest } from '@/components/server/events';
1414
import UptimeDuration from '@/components/server/UptimeDuration';
1515
import StatBlock from '@/components/server/console/StatBlock';
1616
import useWebsocketEvent from '@/plugins/useWebsocketEvent';
17+
import classNames from 'classnames';
1718

1819
type Stats = Record<'memory' | 'cpu' | 'disk' | 'uptime' | 'rx' | 'tx', number>;
1920

20-
interface DetailBlockProps {
21-
className?: string;
22-
}
23-
2421
const getBackgroundColor = (value: number, max: number | null): string | undefined => {
2522
const delta = !max ? 0 : (value / max);
2623

@@ -34,7 +31,7 @@ const getBackgroundColor = (value: number, max: number | null): string | undefin
3431
return undefined;
3532
};
3633

37-
const ServerDetailsBlock = ({ className }: DetailBlockProps) => {
34+
const ServerDetailsBlock = ({ className }: { className?: string }) => {
3835
const [ stats, setStats ] = useState<Stats>({ memory: 0, cpu: 0, disk: 0, uptime: 0, tx: 0, rx: 0 });
3936

4037
const status = ServerContext.useStoreState(state => state.status.value);
@@ -74,7 +71,7 @@ const ServerDetailsBlock = ({ className }: DetailBlockProps) => {
7471
});
7572

7673
return (
77-
<div className={className}>
74+
<div className={classNames('grid grid-cols-6 gap-2 md:gap-4', className)}>
7875
<StatBlock
7976
icon={faClock}
8077
title={'Uptime'}

resources/scripts/components/server/console/StatBlock.tsx

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,40 +3,34 @@ import Icon from '@/components/elements/Icon';
33
import { IconDefinition } from '@fortawesome/free-solid-svg-icons';
44
import classNames from 'classnames';
55
import Tooltip from '@/components/elements/tooltip/Tooltip';
6+
import styles from './style.module.css';
67

78
interface StatBlockProps {
89
title: string;
910
description?: string;
1011
color?: string | undefined;
1112
icon: IconDefinition;
1213
children: React.ReactNode;
14+
className?: string;
1315
}
1416

15-
export default ({ title, icon, color, description, children }: StatBlockProps) => {
17+
export default ({ title, icon, color, description, className, children }: StatBlockProps) => {
1618
return (
1719
<Tooltip arrow placement={'top'} disabled={!description} content={description || ''}>
18-
<div className={'flex items-center space-x-4 bg-gray-600 rounded p-4 shadow-lg'}>
19-
<div
20-
className={classNames(
21-
'transition-colors duration-500',
22-
'flex-shrink-0 flex items-center justify-center w-12 h-12 rounded-lg shadow-md',
23-
color || 'bg-gray-700',
24-
)}
25-
>
20+
<div className={classNames(styles.stat_block, 'bg-gray-600', className)}>
21+
<div className={classNames(styles.status_bar, color || 'bg-gray-700')}/>
22+
<div className={classNames(styles.icon, color || 'bg-gray-700')}>
2623
<Icon
2724
icon={icon}
28-
className={classNames(
29-
'w-6 h-6 m-auto',
30-
{
31-
'text-gray-100': !color || color === 'bg-gray-700',
32-
'text-gray-50': color && color !== 'bg-gray-700',
33-
},
34-
)}
25+
className={classNames({
26+
'text-gray-100': !color || color === 'bg-gray-700',
27+
'text-gray-50': color && color !== 'bg-gray-700',
28+
})}
3529
/>
3630
</div>
3731
<div className={'flex flex-col justify-center overflow-hidden'}>
38-
<p className={'font-header leading-tight text-sm text-gray-200'}>{title}</p>
39-
<p className={'text-xl font-semibold text-gray-50 truncate'}>
32+
<p className={'font-header leading-tight text-xs md:text-sm text-gray-200'}>{title}</p>
33+
<p className={'text-base md:text-xl font-semibold text-gray-50 truncate'}>
4034
{children}
4135
</p>
4236
</div>

resources/scripts/components/server/StatGraphs.tsx renamed to resources/scripts/components/server/console/StatGraphs.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ export default () => {
123123
});
124124

125125
return (
126-
<div css={tw`mt-4 grid grid-cols-1 sm:grid-cols-2 gap-4`}>
126+
<>
127127
<TitledGreyBox title={'Memory usage'} icon={faMemory}>
128128
{status !== 'offline' ?
129129
<canvas
@@ -165,6 +165,6 @@ export default () => {
165165
</p>
166166
}
167167
</TitledGreyBox>
168-
</div>
168+
</>
169169
);
170170
};

0 commit comments

Comments
 (0)