mirror of
https://github.com/pybricks/pybricks-code.git
synced 2026-09-12 01:23:52 +00:00
switch to blueprintjs UI toolkit
This commit is contained in:
committed by
David Lechner
parent
d0f5a42c85
commit
d9211b5b10
@@ -1,12 +1,9 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2020 The Pybricks Authors
|
||||
|
||||
import { Button, Intent, Position, Tooltip } from '@blueprintjs/core';
|
||||
import { WithI18nProps, withI18n } from '@shopify/react-i18n';
|
||||
import React from 'react';
|
||||
import Button from 'react-bootstrap/Button';
|
||||
import Image from 'react-bootstrap/Image';
|
||||
import OverlayTrigger from 'react-bootstrap/OverlayTrigger';
|
||||
import Tooltip from 'react-bootstrap/Tooltip';
|
||||
import { TooltipId } from './button';
|
||||
import en from './button.en.json';
|
||||
|
||||
@@ -28,16 +25,12 @@ type Props = ActionButtonProps & WithI18nProps;
|
||||
class ActionButton extends React.Component<Props> {
|
||||
render(): JSX.Element {
|
||||
return (
|
||||
<OverlayTrigger
|
||||
placement="bottom"
|
||||
overlay={
|
||||
<Tooltip id={`${this.props.id}-tooltip`}>
|
||||
{this.props.i18n.translate(this.props.tooltip)}.
|
||||
</Tooltip>
|
||||
}
|
||||
<Tooltip
|
||||
content={this.props.i18n.translate(this.props.tooltip)}
|
||||
position={Position.BOTTOM}
|
||||
>
|
||||
<Button
|
||||
variant="light"
|
||||
intent={Intent.PRIMARY}
|
||||
onClick={(): void => this.props.onAction()}
|
||||
disabled={this.props.enabled === false}
|
||||
style={
|
||||
@@ -46,9 +39,9 @@ class ActionButton extends React.Component<Props> {
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
<Image src={this.props.icon} alt={this.props.id} />
|
||||
<img src={this.props.icon} alt={this.props.id} />
|
||||
</Button>
|
||||
</OverlayTrigger>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+67
-33
@@ -1,50 +1,84 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2020 The Pybricks Authors
|
||||
|
||||
import React from 'react';
|
||||
import { Col, Row } from 'react-bootstrap';
|
||||
import Container from 'react-bootstrap/Container';
|
||||
import React, { EffectCallback, useEffect, useState } from 'react';
|
||||
import SplitterLayout from 'react-splitter-layout';
|
||||
import Editor from './Editor';
|
||||
import StatusBar from './StatusBar';
|
||||
import Terminal from './Terminal';
|
||||
import Toolbar from './Toolbar';
|
||||
|
||||
import 'react-splitter-layout/lib/index.css';
|
||||
|
||||
function useShowDocs(): boolean {
|
||||
function getShowDocs(): boolean {
|
||||
return window.innerWidth >= 1024;
|
||||
}
|
||||
|
||||
const [showDocs, setShowDocs] = useState(getShowDocs);
|
||||
|
||||
useEffect((): ReturnType<EffectCallback> => {
|
||||
function handleResize(): void {
|
||||
setShowDocs(getShowDocs());
|
||||
}
|
||||
|
||||
window.addEventListener('resize', handleResize);
|
||||
|
||||
return (): void => window.removeEventListener('resize', handleResize);
|
||||
}, []); // Empty array ensures that effect is only run on mount and unmount
|
||||
|
||||
return showDocs;
|
||||
}
|
||||
|
||||
function App(): JSX.Element {
|
||||
const showDocs = useShowDocs();
|
||||
const [dragging, setDragging] = useState(false);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Container fluid>
|
||||
<Row className="vh-100">
|
||||
<Col md={12} lg={8} className="d-flex flex-column container-col">
|
||||
<Row>
|
||||
<Col>
|
||||
<Toolbar />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row className="row flex-grow-1">
|
||||
<Col>
|
||||
<Editor />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col className="terminal py-2">
|
||||
<Terminal />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row className="mt-2">
|
||||
<Col>
|
||||
<StatusBar />
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
<Col lg={4} className="embed-responsive d-lg-block d-md-none">
|
||||
<div className="app">
|
||||
<Toolbar />
|
||||
<SplitterLayout
|
||||
customClassName="app-main"
|
||||
onDragStart={(): void => setDragging(true)}
|
||||
onDragEnd={(): void => setDragging(false)}
|
||||
percentage={true}
|
||||
secondaryInitialSize={Number(
|
||||
localStorage.getItem('app-main-split') || 30,
|
||||
)}
|
||||
onSecondaryPaneSizeChange={(value): void =>
|
||||
localStorage.setItem('app-main-split', String(value))
|
||||
}
|
||||
>
|
||||
<SplitterLayout
|
||||
vertical={true}
|
||||
percentage={true}
|
||||
secondaryInitialSize={Number(
|
||||
localStorage.getItem('app-docs-split') || 30,
|
||||
)}
|
||||
onSecondaryPaneSizeChange={(value): void =>
|
||||
localStorage.setItem('app-docs-split', String(value))
|
||||
}
|
||||
>
|
||||
<Editor />
|
||||
<div className="terminal-padding">
|
||||
<Terminal />
|
||||
</div>
|
||||
</SplitterLayout>
|
||||
{showDocs && (
|
||||
<div className="docs-iframe">
|
||||
{dragging && <div className="docs-iframe-overlay" />}
|
||||
<iframe
|
||||
src="https://docs.pybricks.com"
|
||||
allowFullScreen={true}
|
||||
title="docs"
|
||||
></iframe>
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
width="100%"
|
||||
height="100%"
|
||||
frameBorder="none"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</SplitterLayout>
|
||||
<StatusBar />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
+31
-10
@@ -1,9 +1,12 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2020 The Pybricks Authors
|
||||
|
||||
import React, { ReactElement } from 'react';
|
||||
import { ResizeSensor } from '@blueprintjs/core';
|
||||
import { Ace } from 'ace-builds';
|
||||
import React from 'react';
|
||||
import AceEditor from 'react-ace';
|
||||
import { ReactReduxContext } from 'react-redux';
|
||||
import { connect } from 'react-redux';
|
||||
import { Action, Dispatch } from '../actions';
|
||||
import { setEditSession } from '../actions/editor';
|
||||
|
||||
import 'ace-builds/src-noconflict/mode-python';
|
||||
@@ -11,16 +14,30 @@ import 'ace-builds/src-noconflict/theme-xcode';
|
||||
import 'ace-builds/src-min-noconflict/ext-searchbox';
|
||||
import 'ace-builds/src-min-noconflict/ext-language_tools';
|
||||
|
||||
class Editor extends React.Component {
|
||||
type DispatchProps = { onSessionChanged: (session?: Ace.EditSession) => void };
|
||||
|
||||
type EditorProps = DispatchProps;
|
||||
|
||||
class Editor extends React.Component<EditorProps> {
|
||||
private editorRef: React.RefObject<AceEditor>;
|
||||
|
||||
constructor(props: EditorProps) {
|
||||
super(props);
|
||||
this.editorRef = React.createRef();
|
||||
}
|
||||
|
||||
render(): JSX.Element {
|
||||
return (
|
||||
<ReactReduxContext.Consumer>
|
||||
{({ store }): ReactElement => (
|
||||
<ResizeSensor
|
||||
onResize={(): void => this.editorRef.current?.editor?.resize()}
|
||||
>
|
||||
<div className="editor-container">
|
||||
<AceEditor
|
||||
ref={this.editorRef}
|
||||
mode="python"
|
||||
theme="xcode"
|
||||
fontSize="16pt"
|
||||
width="100"
|
||||
width="100%"
|
||||
height="100%"
|
||||
focus={true}
|
||||
placeholder="Write your program here..."
|
||||
@@ -31,7 +48,7 @@ class Editor extends React.Component {
|
||||
enableLiveAutocompletion: true,
|
||||
}}
|
||||
onFocus={(_, e): void => {
|
||||
store.dispatch(setEditSession(e?.session));
|
||||
this.props.onSessionChanged(e?.session);
|
||||
}}
|
||||
onChange={(v): void => localStorage.setItem('program', v)}
|
||||
commands={[
|
||||
@@ -47,10 +64,14 @@ class Editor extends React.Component {
|
||||
},
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
</ReactReduxContext.Consumer>
|
||||
</div>
|
||||
</ResizeSensor>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Editor;
|
||||
const mapDispatchToProps = (dispatch: Dispatch): DispatchProps => ({
|
||||
onSessionChanged: (s): Action => dispatch(setEditSession(s)),
|
||||
});
|
||||
|
||||
export default connect(undefined, mapDispatchToProps)(Editor);
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2020 The Pybricks Authors
|
||||
|
||||
import { IconName, Intent, Toast } from '@blueprintjs/core';
|
||||
import { WithI18nProps, withI18n } from '@shopify/react-i18n';
|
||||
import React from 'react';
|
||||
import Toast from 'react-bootstrap/Toast';
|
||||
import { connect } from 'react-redux';
|
||||
import { Dispatch } from '../actions';
|
||||
import { remove } from '../actions/notification';
|
||||
import { NotificationLevel, remove } from '../actions/notification';
|
||||
import { Level } from '../reducers/notification';
|
||||
import en from './notification.en.json';
|
||||
|
||||
interface DispatchProps {
|
||||
@@ -15,7 +16,7 @@ interface DispatchProps {
|
||||
|
||||
interface OwnProps {
|
||||
id: number;
|
||||
style: string;
|
||||
level: NotificationLevel;
|
||||
message?: string;
|
||||
messageId?: string;
|
||||
helpUrl?: string;
|
||||
@@ -23,51 +24,63 @@ interface OwnProps {
|
||||
|
||||
type NotificationProps = DispatchProps & OwnProps & WithI18nProps;
|
||||
|
||||
function mapTitle(style: string): string {
|
||||
switch (style) {
|
||||
case 'danger':
|
||||
return 'Error';
|
||||
case 'warning':
|
||||
return 'Warning';
|
||||
function mapIntent(level: NotificationLevel): Intent {
|
||||
switch (level) {
|
||||
case Level.Error:
|
||||
return Intent.DANGER;
|
||||
case Level.Warning:
|
||||
return Intent.WARNING;
|
||||
case Level.Info:
|
||||
return Intent.PRIMARY;
|
||||
default:
|
||||
return 'Info';
|
||||
return Intent.NONE;
|
||||
}
|
||||
}
|
||||
|
||||
function mapIcon(level: NotificationLevel): IconName | undefined {
|
||||
switch (level) {
|
||||
case Level.Error:
|
||||
return 'error';
|
||||
case Level.Warning:
|
||||
return 'warning-sign';
|
||||
case Level.Info:
|
||||
return 'info-sign';
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
class Notification extends React.Component<NotificationProps> {
|
||||
render(): JSX.Element {
|
||||
const title = mapTitle(this.props.style);
|
||||
return (
|
||||
<Toast
|
||||
onClose={(): void => {
|
||||
onDismiss={(): void => {
|
||||
this.props.onClose();
|
||||
}}
|
||||
transition={false}
|
||||
>
|
||||
<Toast.Header>
|
||||
<strong className={`mr-auto text-${this.props.style}`}>
|
||||
{title}
|
||||
</strong>
|
||||
</Toast.Header>
|
||||
<Toast.Body>
|
||||
<p>
|
||||
{this.props.messageId
|
||||
? this.props.i18n.translate(this.props.messageId)
|
||||
: this.props.message || 'missing message!'}
|
||||
</p>
|
||||
<p>
|
||||
timeout={0}
|
||||
intent={mapIntent(this.props.level)}
|
||||
icon={mapIcon(this.props.level)}
|
||||
message={
|
||||
<div>
|
||||
<p>
|
||||
{this.props.messageId
|
||||
? this.props.i18n.translate(this.props.messageId)
|
||||
: this.props.message || 'missing message!'}
|
||||
</p>
|
||||
{this.props.helpUrl && (
|
||||
<a
|
||||
href={this.props.helpUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
More info
|
||||
</a>
|
||||
<p>
|
||||
<a
|
||||
href={this.props.helpUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
More info
|
||||
</a>
|
||||
</p>
|
||||
)}
|
||||
</p>
|
||||
</Toast.Body>
|
||||
</Toast>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2020 The Pybricks Authors
|
||||
|
||||
import { Toaster } from '@blueprintjs/core';
|
||||
import React from 'react';
|
||||
import { Collapse } from 'react-bootstrap';
|
||||
import { connect } from 'react-redux';
|
||||
import { TransitionGroup } from 'react-transition-group';
|
||||
import { RootState } from '../reducers';
|
||||
import { Level, NotificationList } from '../reducers/notification';
|
||||
import { NotificationList } from '../reducers/notification';
|
||||
import Notification from './Notification';
|
||||
|
||||
interface StateProps {
|
||||
@@ -15,45 +14,21 @@ interface StateProps {
|
||||
|
||||
type NotificationStackProps = StateProps;
|
||||
|
||||
function mapLevelToStyle(level: Level): string {
|
||||
switch (level) {
|
||||
case Level.Error:
|
||||
return 'danger';
|
||||
case Level.Warning:
|
||||
return 'warning';
|
||||
case Level.Info:
|
||||
return 'info';
|
||||
}
|
||||
}
|
||||
|
||||
class NotificationStack extends React.Component<NotificationStackProps> {
|
||||
render(): JSX.Element {
|
||||
return (
|
||||
<div aria-live="polite" aria-atomic="true" style={{ position: 'relative' }}>
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: '10px',
|
||||
right: '10px',
|
||||
minWidth: '350px',
|
||||
zIndex: 999,
|
||||
}}
|
||||
>
|
||||
<TransitionGroup>
|
||||
{this.props.list.map((n) => (
|
||||
<Collapse key={n.id} in={true}>
|
||||
<Notification
|
||||
id={n.id}
|
||||
style={mapLevelToStyle(n.level)}
|
||||
message={n.message}
|
||||
messageId={n.messageId}
|
||||
helpUrl={n.helpUrl}
|
||||
/>
|
||||
</Collapse>
|
||||
))}
|
||||
</TransitionGroup>
|
||||
</div>
|
||||
</div>
|
||||
<Toaster>
|
||||
{this.props.list.map((n) => (
|
||||
<Notification
|
||||
id={n.id}
|
||||
key={n.id}
|
||||
level={n.level}
|
||||
message={n.message}
|
||||
messageId={n.messageId}
|
||||
helpUrl={n.helpUrl}
|
||||
/>
|
||||
))}
|
||||
</Toaster>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2020 The Pybricks Authors
|
||||
|
||||
import { Button, Intent, Position, Tooltip } from '@blueprintjs/core';
|
||||
import { WithI18nProps, withI18n } from '@shopify/react-i18n';
|
||||
import React from 'react';
|
||||
import Button from 'react-bootstrap/Button';
|
||||
import Image from 'react-bootstrap/Image';
|
||||
import OverlayTrigger from 'react-bootstrap/OverlayTrigger';
|
||||
import Tooltip from 'react-bootstrap/Tooltip';
|
||||
import Dropzone, { FileRejection } from 'react-dropzone';
|
||||
import { TooltipId } from './button';
|
||||
import en from './button.en.json';
|
||||
@@ -81,17 +78,13 @@ class OpenFileButton extends React.Component<Props> {
|
||||
noKeyboard={this.props.onClick !== undefined}
|
||||
>
|
||||
{({ getRootProps, getInputProps }): JSX.Element => (
|
||||
<OverlayTrigger
|
||||
placement="bottom"
|
||||
overlay={
|
||||
<Tooltip id={`${this.props.id}-tooltip`}>
|
||||
{this.props.i18n.translate(this.props.tooltip)}.
|
||||
</Tooltip>
|
||||
}
|
||||
<Tooltip
|
||||
content={this.props.i18n.translate(this.props.tooltip)}
|
||||
position={Position.BOTTOM}
|
||||
>
|
||||
<Button
|
||||
{...getRootProps()}
|
||||
variant="light"
|
||||
intent={Intent.PRIMARY}
|
||||
disabled={this.props.enabled === false}
|
||||
style={
|
||||
this.props.enabled === false
|
||||
@@ -106,9 +99,9 @@ class OpenFileButton extends React.Component<Props> {
|
||||
: {})}
|
||||
>
|
||||
<input {...getInputProps()} />
|
||||
<Image src={this.props.icon} alt={this.props.id} />
|
||||
<img src={this.props.icon} alt={this.props.id} />
|
||||
</Button>
|
||||
</OverlayTrigger>
|
||||
</Tooltip>
|
||||
)}
|
||||
</Dropzone>
|
||||
);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2020 The Pybricks Authors
|
||||
|
||||
import { ProgressBar } from '@blueprintjs/core';
|
||||
import React from 'react';
|
||||
import ProgressBar from 'react-bootstrap/ProgressBar';
|
||||
import { connect } from 'react-redux';
|
||||
import { RootState } from '../reducers';
|
||||
|
||||
@@ -13,8 +13,12 @@ type StatusProps = StateProps;
|
||||
class StatusBar extends React.Component<StatusProps> {
|
||||
render(): JSX.Element {
|
||||
return (
|
||||
<div className="status-bar p-2">
|
||||
<ProgressBar className="w-25" now={this.props.progress} />
|
||||
<div className="status-bar">
|
||||
<ProgressBar
|
||||
className="status-bar-item"
|
||||
value={this.props.progress}
|
||||
animate={false}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2020 The Pybricks Authors
|
||||
|
||||
import { ResizeSensor } from '@blueprintjs/core';
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import ResizeObserver from 'react-resize-observer';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { Terminal as XTerm } from 'xterm';
|
||||
import { FitAddon } from 'xterm-addon-fit';
|
||||
@@ -62,13 +62,9 @@ class Terminal extends React.Component<TerminalProps> {
|
||||
|
||||
render(): JSX.Element {
|
||||
return (
|
||||
<div
|
||||
id="terminal"
|
||||
ref={this.terminalRef}
|
||||
style={{ height: 'inherit', width: 'inherit' }}
|
||||
>
|
||||
<ResizeObserver onResize={(): void => this.fitAddon.fit()} />
|
||||
</div>
|
||||
<ResizeSensor onResize={(): void => this.fitAddon.fit()}>
|
||||
<div className="terminal-container" ref={this.terminalRef} />
|
||||
</ResizeSensor>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+20
-17
@@ -1,9 +1,8 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2020 The Pybricks Authors
|
||||
|
||||
import { ButtonGroup, Navbar } from '@blueprintjs/core';
|
||||
import React from 'react';
|
||||
import ButtonGroup from 'react-bootstrap/ButtonGroup';
|
||||
import ButtonToolbar from 'react-bootstrap/ButtonToolbar';
|
||||
import BluetoothButton from './BluetoothButton';
|
||||
import FlashButton from './FlashButton';
|
||||
import OpenButton from './OpenButton';
|
||||
@@ -15,21 +14,25 @@ import StopButton from './StopButton';
|
||||
class Toolbar extends React.Component {
|
||||
render(): JSX.Element {
|
||||
return (
|
||||
<ButtonToolbar className="m-2">
|
||||
<ButtonGroup className="mr-2" size="lg">
|
||||
<OpenButton id="open" />
|
||||
<SaveAsButton id="saveAs" />
|
||||
</ButtonGroup>
|
||||
<ButtonGroup className="mr-2" size="lg">
|
||||
<BluetoothButton id="bluetooth" />
|
||||
<RunButton id="run" />
|
||||
<StopButton id="stop" />
|
||||
</ButtonGroup>
|
||||
<ButtonGroup className="mr-2" size="lg">
|
||||
<ReplButton id="repl" />
|
||||
<FlashButton id="flash" />
|
||||
</ButtonGroup>
|
||||
</ButtonToolbar>
|
||||
<Navbar fixedToTop={true}>
|
||||
<Navbar.Group>
|
||||
<ButtonGroup>
|
||||
<OpenButton id="open" />
|
||||
<SaveAsButton id="saveAs" />
|
||||
</ButtonGroup>
|
||||
<Navbar.Divider />
|
||||
<ButtonGroup>
|
||||
<BluetoothButton id="bluetooth" />
|
||||
<RunButton id="run" />
|
||||
<StopButton id="stop" />
|
||||
</ButtonGroup>
|
||||
<Navbar.Divider />
|
||||
<ButtonGroup>
|
||||
<ReplButton id="repl" />
|
||||
<FlashButton id="flash" />
|
||||
</ButtonGroup>
|
||||
</Navbar.Group>
|
||||
</Navbar>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+79
-19
@@ -1,36 +1,96 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2020 The Pybricks Authors
|
||||
|
||||
// Override default variables before the import
|
||||
$body-bg: #e8e8e8;
|
||||
@import '@blueprintjs/core/lib/scss/variables.scss';
|
||||
|
||||
// Import Bootstrap and its default variables
|
||||
@import '~bootstrap/scss/bootstrap';
|
||||
// override variables here
|
||||
// See https://blueprintjs.com/docs/#core/variables
|
||||
|
||||
.terminal {
|
||||
height: 15vh;
|
||||
$pt-navbar-height: 72px;
|
||||
|
||||
$pybricks-blue: #0088ce;
|
||||
$pt-intent-primary: $pybricks-blue;
|
||||
$pt-outline-color: rgba($pybricks-blue, 0.6);
|
||||
|
||||
$navbar-backgound-color: $pt-app-background-color;
|
||||
$dark-navbar-background-color: $pt-dark-app-background-color;
|
||||
|
||||
@import '~normalize.css';
|
||||
@import '@blueprintjs/core/src/blueprint.scss';
|
||||
|
||||
$status-bar-height: 3vh;
|
||||
|
||||
:root {
|
||||
--mobile-pad: 0px;
|
||||
}
|
||||
|
||||
.container-col {
|
||||
padding: 0px;
|
||||
padding-right: 8px;
|
||||
body {
|
||||
// no scrolling of the page
|
||||
overflow: hidden;
|
||||
// See https://blueprintjs.com/docs/#core/components/navbar
|
||||
padding-top: $pt-navbar-height;
|
||||
}
|
||||
|
||||
.docs-iframe {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.docs-iframe-overlay {
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.editor-container {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.terminal-padding {
|
||||
padding-left: 10px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.terminal-container {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.app-main {
|
||||
// height makes everything fit without scrolling
|
||||
height: calc(100vh - #{$pt-navbar-height} - #{$status-bar-height} - var(--mobile-pad)) !important;
|
||||
}
|
||||
|
||||
.status-bar {
|
||||
background-color: #0088ce;
|
||||
position: fixed;
|
||||
top: calc(100vh - #{$status-bar-height} - var(--mobile-pad));
|
||||
background-color: $pybricks-blue;
|
||||
height: $status-bar-height;
|
||||
width: 100vw;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.btn-light, .btn-light:focus {
|
||||
background-color: #0088ce;
|
||||
border-width: 0px;
|
||||
.status-bar-item {
|
||||
width: 25%;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.btn-light:disabled {
|
||||
background-color: #65acd0;
|
||||
border-width: 0px;
|
||||
.bp3-progress-bar.status-bar-item {
|
||||
// override progress bar default gray1 backgound
|
||||
background-color: $pt-app-background-color;
|
||||
}
|
||||
|
||||
.btn-light:hover, .btn-light:active {
|
||||
background-color: #005885 !important;
|
||||
border-width: 0px;
|
||||
.bp3-navbar-divider {
|
||||
// don't draw vertical line since we are just using button groups
|
||||
border-left: unset;
|
||||
}
|
||||
|
||||
.ace_gutter {
|
||||
// make ace editor match app backgound color
|
||||
background-color: $pt-app-background-color !important;
|
||||
}
|
||||
|
||||
.layout-splitter {
|
||||
// make layout splitter match app color scheme
|
||||
background-color: $pt-divider-black !important;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2020 The Pybricks Authors
|
||||
|
||||
import { ResizeSensor } from '@blueprintjs/core';
|
||||
import { I18nContext, I18nManager } from '@shopify/react-i18n';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
@@ -46,6 +47,21 @@ ReactDOM.render(
|
||||
<React.StrictMode>
|
||||
<Provider store={store}>
|
||||
<I18nContext.Provider value={i18n}>
|
||||
{/* This is a hack for correctly sizing to view height on mobile when not running in fullscreen mode. */}
|
||||
{/* https://css-tricks.com/the-trick-to-viewport-units-on-mobile/ */}
|
||||
<ResizeSensor
|
||||
onResize={(e): void => {
|
||||
document.documentElement.style.setProperty(
|
||||
'--mobile-pad',
|
||||
`${e[0].contentRect.height - window.innerHeight}px`,
|
||||
);
|
||||
}}
|
||||
>
|
||||
<div
|
||||
id="vh"
|
||||
style={{ height: '100vh', width: '0px', position: 'absolute' }}
|
||||
/>
|
||||
</ResizeSensor>
|
||||
<NotificationStack />
|
||||
<App />
|
||||
</I18nContext.Provider>
|
||||
|
||||
@@ -8,7 +8,7 @@ import { BootloaderAction, BootloaderActionType } from '../actions/lwp3-bootload
|
||||
const progress: Reducer<number, BootloaderAction> = (state = -1, action) => {
|
||||
switch (action.type) {
|
||||
case BootloaderActionType.FlashProgress:
|
||||
return (action.complete / action.total) * 100;
|
||||
return action.complete / action.total;
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user