aboutsummaryrefslogtreecommitdiffstats
path: root/packages/dev-tools-pages/ts/components/Code.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'packages/dev-tools-pages/ts/components/Code.tsx')
-rw-r--r--packages/dev-tools-pages/ts/components/Code.tsx114
1 files changed, 114 insertions, 0 deletions
diff --git a/packages/dev-tools-pages/ts/components/Code.tsx b/packages/dev-tools-pages/ts/components/Code.tsx
new file mode 100644
index 000000000..42d4234f1
--- /dev/null
+++ b/packages/dev-tools-pages/ts/components/Code.tsx
@@ -0,0 +1,114 @@
+import * as React from 'react';
+import styled from 'styled-components';
+
+import BaseButton from './Button';
+
+interface CodeProps {
+ children: React.ReactNode;
+ language?: string;
+}
+
+interface CodeState {
+ hlCode?: string;
+ copied?: boolean;
+}
+
+const Button = styled(BaseButton)`
+ opacity: 0;
+ position: absolute;
+ top: 1rem;
+ right: 1rem;
+ transition: opacity 0.2s;
+`;
+
+const Base =
+ styled.div <
+ CodeProps >
+ `
+ color: ${props => (props.language === undefined ? '#FFF' : 'inherit')};
+ background-color: ${props => (props.language === undefined ? '#000' : '#F1F4F5')};
+ white-space: ${props => (props.language === undefined ? 'nowrap' : '')};
+ position: relative;
+ &:hover ${Button} {
+ opacity: 1;
+ }
+`;
+
+const StyledCode = styled.code`
+ padding: 1.5rem;
+ display: block;
+ overflow-y: scroll;
+ -webkit-overflow-scrolling: touch;
+`;
+
+const StyledPre = styled.pre`
+ margin: 0;
+`;
+
+const StyledCopyInput = styled.textarea`
+ height: 0;
+ position: absolute;
+ top: 0;
+ right: 0;
+ z-index: -1;
+`;
+
+const CopyInput = StyledCopyInput as any;
+
+class Code extends React.Component<CodeProps, CodeState> {
+ code = React.createRef<HTMLTextAreaElement>();
+
+ state: CodeState = {};
+
+ constructor(props: CodeProps) {
+ super(props);
+ }
+
+ async componentDidMount() {
+ const { language, children } = this.props;
+
+ if (language !== undefined) {
+ const { highlight } = await import(/* webpackChunkName: 'highlight.js' */ 'highlight.js');
+ const { value: hlCode } = highlight(language, children as string);
+ this.setState({ hlCode });
+ }
+ }
+
+ handleCopy = async () => {
+ try {
+ if ('clipboard' in navigator) {
+ await (navigator as any).clipboard.writeText(this.props.children);
+ this.setState({ copied: true });
+ } else {
+ this.code.current.focus();
+ this.code.current.select();
+ document.execCommand('copy');
+ this.setState({ copied: true });
+ }
+ } catch (error) {
+ this.setState({ copied: false });
+ }
+ };
+
+ render() {
+ const { language, children } = this.props;
+
+ return (
+ <Base language={language}>
+ <StyledPre>
+ {this.state.hlCode !== undefined ? (
+ <StyledCode dangerouslySetInnerHTML={{ __html: this.state.hlCode }} />
+ ) : (
+ <StyledCode>{this.props.children}</StyledCode>
+ )}
+ </StyledPre>
+ <Button onClick={this.handleCopy}>Copy</Button>
+ {!('clipboard' in navigator) ? (
+ <CopyInput readOnly aria-hidden="true" ref={this.code} value={children} />
+ ) : null}
+ </Base>
+ );
+ }
+}
+
+export default Code;