Initial Move from Azure DevOps to GitHub

This commit is contained in:
Steve Faulkner
2020-05-25 21:30:55 -05:00
commit 36581fb6d9
986 changed files with 195242 additions and 0 deletions

View File

@@ -0,0 +1,97 @@
import { AppState, ContentRef, selectors } from "@nteract/core";
import * as React from "react";
import { connect } from "react-redux";
import styled from "styled-components";
import NotebookRenderer from "../../../NotebookRenderer/NotebookRenderer";
import * as TextFile from "./text-file";
const PaddedContainer = styled.div`
padding-left: var(--nt-spacing-l, 10px);
padding-top: var(--nt-spacing-m, 10px);
padding-right: var(--nt-spacing-m, 10px);
`;
const JupyterExtensionContainer = styled.div`
display: flex;
flex-flow: column;
align-items: stretch;
height: 100%;
`;
const JupyterExtensionChoiceContainer = styled.div`
flex: 1 1 auto;
overflow: auto;
`;
interface FileProps {
type: "notebook" | "file" | "dummy";
contentRef: ContentRef;
mimetype?: string | null;
}
export class File extends React.PureComponent<FileProps> {
getChoice = () => {
let choice = null;
// notebooks don't report a mimetype so we'll use the content.type
if (this.props.type === "notebook") {
choice = <NotebookRenderer contentRef={this.props.contentRef} />;
} else if (this.props.type === "dummy") {
choice = null;
} else if (this.props.mimetype == null || !TextFile.handles(this.props.mimetype)) {
// This should not happen as we intercept mimetype upstream, but just in case
choice = (
<PaddedContainer>
<pre>
This file type cannot be rendered. Please download the file, in order to view it outside of Data Explorer.
</pre>
</PaddedContainer>
);
} else {
choice = <TextFile.default contentRef={this.props.contentRef} />;
}
return choice;
};
render(): JSX.Element {
const choice = this.getChoice();
// Right now we only handle one kind of editor
// If/when we support more modes, we would case them off here
return (
<React.Fragment>
<JupyterExtensionContainer>
<JupyterExtensionChoiceContainer>{choice}</JupyterExtensionChoiceContainer>
</JupyterExtensionContainer>
</React.Fragment>
);
}
}
interface InitialProps {
contentRef: ContentRef;
}
// Since the contentRef stays unique for the duration of this file,
// we use the makeMapStateToProps pattern to optimize re-render
const makeMapStateToProps = (initialState: AppState, initialProps: InitialProps) => {
const { contentRef } = initialProps;
const mapStateToProps = (state: AppState) => {
const content = selectors.content(state, initialProps);
return {
contentRef,
mimetype: content.mimetype,
type: content.type
};
};
return mapStateToProps;
};
export const ConnectedFile = connect(makeMapStateToProps)(File);
export default ConnectedFile;

View File

@@ -0,0 +1,143 @@
import { StringUtils } from "../../../../../Utils/StringUtils";
import { actions, AppState, ContentRef, selectors } from "@nteract/core";
import { MonacoEditorProps } from "@nteract/monaco-editor";
import * as React from "react";
import { connect } from "react-redux";
import { Dispatch } from "redux";
import styled from "styled-components";
const EditorContainer = styled.div`
position: absolute;
left: 0;
height: 100%;
width: 100%;
.monaco {
height: 100%;
}
`;
interface MappedStateProps {
mimetype: string;
text: string;
contentRef: ContentRef;
theme: string; // "light" | "dark";
}
interface MappedDispatchProps {
handleChange: (value: string) => void;
}
type TextFileProps = MappedStateProps & MappedDispatchProps;
interface TextFileState {
Editor: React.ComponentType<MonacoEditorProps>;
}
class EditorPlaceholder extends React.PureComponent<MonacoEditorProps> {
render(): JSX.Element {
// TODO: Show a little blocky placeholder
return null;
}
}
export class TextFile extends React.PureComponent<TextFileProps, TextFileState> {
constructor(props: TextFileProps) {
super(props);
this.state = {
Editor: EditorPlaceholder
};
}
handleChange = (source: string) => {
this.props.handleChange(source);
};
componentDidMount(): void {
import(/* webpackChunkName: "monaco-editor" */ "@nteract/monaco-editor").then(module => {
this.setState({ Editor: module.default });
});
}
render(): JSX.Element {
const Editor = this.state.Editor;
return (
<EditorContainer className="nteract-editor" style={{ position: "static" }}>
<Editor
theme={this.props.theme === "dark" ? "vs-dark" : "vs"}
mode={this.props.mimetype}
editorFocused
value={this.props.text}
onChange={this.handleChange.bind(this)}
/>
</EditorContainer>
);
}
}
interface InitialProps {
contentRef: ContentRef;
}
function makeMapStateToTextFileProps(
initialState: AppState,
initialProps: InitialProps
): (state: AppState) => MappedStateProps {
const { contentRef } = initialProps;
const mapStateToTextFileProps = (state: AppState) => {
const content = selectors.content(state, { contentRef });
if (!content || content.type !== "file") {
throw new Error("The text file component must have content");
}
const text = content.model ? content.model.text : "";
return {
contentRef,
mimetype: content.mimetype != null ? content.mimetype : "text/plain",
text,
theme: selectors.currentTheme(state)
};
};
return mapStateToTextFileProps;
}
const makeMapDispatchToTextFileProps = (
initialDispatch: Dispatch,
initialProps: InitialProps
): ((dispatch: Dispatch) => MappedDispatchProps) => {
const { contentRef } = initialProps;
const mapDispatchToTextFileProps = (dispatch: Dispatch) => {
return {
handleChange: (source: string) => {
dispatch(
actions.updateFileText({
contentRef,
text: source
})
);
}
};
};
return mapDispatchToTextFileProps;
};
const ConnectedTextFile = connect<MappedStateProps, MappedDispatchProps, InitialProps, AppState>(
makeMapStateToTextFileProps,
makeMapDispatchToTextFileProps
)(TextFile);
export function handles(mimetype: string) {
return (
!mimetype ||
StringUtils.startsWith(mimetype, "text/") ||
StringUtils.startsWith(mimetype, "application/javascript") ||
StringUtils.startsWith(mimetype, "application/json") ||
StringUtils.startsWith(mimetype, "application/x-ipynb+json")
);
}
export default ConnectedTextFile;

View File

@@ -0,0 +1,173 @@
// Vendor modules
import { CellType, ImmutableNotebook } from "@nteract/commutable";
import { HeaderDataProps } from "@nteract/connected-components/lib/header-editor";
import {
AppState,
ContentRef,
HostRecord,
selectors,
actions,
DirectoryContentRecordProps,
DummyContentRecordProps,
FileContentRecordProps,
NotebookContentRecordProps
} from "@nteract/core";
import { RecordOf } from "immutable";
import * as React from "react";
import { HotKeys, KeyMap } from "react-hotkeys";
import { connect } from "react-redux";
import { Dispatch } from "redux";
// Local modules
import { default as File } from "./file";
interface IContentsBaseProps {
contentRef: ContentRef;
error?: object | null;
}
interface IStateToProps {
headerData?: HeaderDataProps;
}
interface IDispatchFromProps {
handlers?: any;
onHeaderEditorChange?: (props: HeaderDataProps) => void;
}
type ContentsProps = IContentsBaseProps & IStateToProps & IDispatchFromProps;
class Contents extends React.PureComponent<ContentsProps> {
private keyMap: KeyMap = {
CHANGE_CELL_TYPE: ["ctrl+shift+y", "ctrl+shift+m", "meta+shift+y", "meta+shift+m"],
COPY_CELL: ["ctrl+shift+c", "meta+shift+c"],
CREATE_CELL_ABOVE: ["ctrl+shift+a", "meta+shift+a"],
CREATE_CELL_BELOW: ["ctrl+shift+b", "meta+shift+b"],
CUT_CELL: ["ctrl+shift+x", "meta+shift+x"],
DELETE_CELL: ["ctrl+shift+d", "meta+shift+d"],
EXECUTE_ALL_CELLS: ["alt+r a"],
INTERRUPT_KERNEL: ["alt+r i"],
KILL_KERNEL: ["alt+r k"],
OPEN: ["ctrl+o", "meta+o"],
PASTE_CELL: ["ctrl+shift+v"],
RESTART_KERNEL: ["alt+r r", "alt+r c", "alt+r a"],
SAVE: ["ctrl+s", "ctrl+shift+s", "meta+s", "meta+shift+s"]
};
render(): JSX.Element {
const { contentRef, handlers } = this.props;
if (!contentRef) {
return <></>;
}
return (
<React.Fragment>
<HotKeys keyMap={this.keyMap} handlers={handlers} className="hotKeys">
<File contentRef={contentRef} />
</HotKeys>
</React.Fragment>
);
}
}
const makeMapStateToProps: any = (initialState: AppState, initialProps: { contentRef: ContentRef }) => {
const host: HostRecord = initialState.app.host;
if (host.type !== "jupyter") {
throw new Error("this component only works with jupyter apps");
}
const mapStateToProps = (state: AppState): Partial<ContentsProps> => {
const contentRef: ContentRef = initialProps.contentRef;
if (!contentRef) {
throw new Error("cant display without a contentRef");
}
const content:
| RecordOf<NotebookContentRecordProps>
| RecordOf<DummyContentRecordProps>
| RecordOf<FileContentRecordProps>
| RecordOf<DirectoryContentRecordProps>
| undefined = selectors.content(state, { contentRef });
if (!content) {
return {
contentRef: undefined,
error: undefined,
headerData: undefined
};
}
let headerData: HeaderDataProps = {
authors: [],
description: "",
tags: [],
title: ""
};
// If a notebook, we need to read in the headerData if available
if (content.type === "notebook") {
const notebook: ImmutableNotebook = content.model.get("notebook");
const metadata: any = notebook.metadata.toJS();
const { authors = [], description = "", tags = [], title = "" } = metadata;
// Updates
headerData = Object.assign({}, headerData, {
authors,
description,
tags,
title
});
}
return {
contentRef,
error: content.error,
headerData
};
};
return mapStateToProps;
};
const mapDispatchToProps = (dispatch: Dispatch, ownProps: ContentsProps): object => {
const { contentRef } = ownProps;
return {
onHeaderEditorChange: (props: HeaderDataProps) => {
return dispatch(
actions.overwriteMetadataFields({
...props,
contentRef: ownProps.contentRef
})
);
},
// `HotKeys` handlers object
// see: https://github.com/greena13/react-hotkeys#defining-handlers
handlers: {
CHANGE_CELL_TYPE: (event: KeyboardEvent) => {
const type: CellType = event.key === "Y" ? "code" : "markdown";
return dispatch(actions.changeCellType({ to: type, contentRef }));
},
COPY_CELL: () => dispatch(actions.copyCell({ contentRef })),
CREATE_CELL_ABOVE: () => dispatch(actions.createCellAbove({ cellType: "code", contentRef })),
CREATE_CELL_BELOW: () => dispatch(actions.createCellBelow({ cellType: "code", source: "", contentRef })),
CUT_CELL: () => dispatch(actions.cutCell({ contentRef })),
DELETE_CELL: () => dispatch(actions.deleteCell({ contentRef })),
EXECUTE_ALL_CELLS: () => dispatch(actions.executeAllCells({ contentRef })),
INTERRUPT_KERNEL: () => dispatch(actions.interruptKernel({})),
KILL_KERNEL: () => dispatch(actions.killKernel({ restarting: false })),
PASTE_CELL: () => dispatch(actions.pasteCell({ contentRef })),
RESTART_KERNEL: (event: KeyboardEvent) => {
const outputHandling: "None" | "Clear All" | "Run All" =
event.key === "r" ? "None" : event.key === "a" ? "Run All" : "Clear All";
return dispatch(actions.restartKernel({ outputHandling, contentRef }));
},
SAVE: () => dispatch(actions.save({ contentRef }))
}
};
};
export default connect(makeMapStateToProps, mapDispatchToProps)(Contents);