/** @jsx jsx */ import { jsx } from '@emotion/core'; import PropTypes from 'prop-types'; import { formatBytes } from '../utils/format.js'; import { createHTML } from '../utils/markup.js'; import { ContentArea, ContentAreaHeaderBar } from './ContentArea.js'; function getBasename(path) { let segments = path.split('/'); return segments[segments.length - 1]; } function ImageViewer({ path, uri }) { return ( <div css={{ padding: 20, textAlign: 'center' }}> <img alt={getBasename(path)} src={uri} /> </div> ); } function CodeListing({ highlights }) { let lines = highlights.slice(0); let hasTrailingNewline = lines.length && lines[lines.length - 1] === ''; if (hasTrailingNewline) { lines.pop(); } return ( <div className="code-listing" css={{ overflowX: 'auto', overflowY: 'hidden', paddingTop: 5, paddingBottom: 5 }} > <table css={{ border: 'none', borderCollapse: 'collapse', borderSpacing: 0 }} > <tbody> {lines.map((line, index) => { let lineNumber = index + 1; return ( <tr key={index}> <td id={`L${lineNumber}`} css={{ paddingLeft: 10, paddingRight: 10, color: 'rgba(27,31,35,.3)', textAlign: 'right', verticalAlign: 'top', width: '1%', minWidth: 50, userSelect: 'none' }} > <span>{lineNumber}</span> </td> <td id={`LC${lineNumber}`} css={{ paddingLeft: 10, paddingRight: 10, color: '#24292e', whiteSpace: 'pre' }} > <code dangerouslySetInnerHTML={createHTML(line)} /> </td> </tr> ); })} {!hasTrailingNewline && ( <tr key="no-newline"> <td css={{ paddingLeft: 10, paddingRight: 10, color: 'rgba(27,31,35,.3)', textAlign: 'right', verticalAlign: 'top', width: '1%', minWidth: 50, userSelect: 'none' }} > \ </td> <td css={{ paddingLeft: 10, color: 'rgba(27,31,35,.3)', userSelect: 'none' }} > No newline at end of file </td> </tr> )} </tbody> </table> </div> ); } function BinaryViewer() { return ( <div css={{ padding: 20 }}> <p css={{ textAlign: 'center' }}>No preview available.</p> </div> ); } export default function FileViewer({ packageName, packageVersion, path, details }) { let { highlights, uri, language, size } = details; return ( <ContentArea> <ContentAreaHeaderBar> <span>{formatBytes(size)}</span> <span>{language}</span> <span> <a href={`/${packageName}@${packageVersion}${path}`} css={{ display: 'inline-block', marginLeft: 8, padding: '2px 8px', textDecoration: 'none', fontWeight: 600, fontSize: '0.9rem', color: '#24292e', backgroundColor: '#eff3f6', border: '1px solid rgba(27,31,35,.2)', borderRadius: 3, ':hover': { backgroundColor: '#e6ebf1', borderColor: 'rgba(27,31,35,.35)' }, ':active': { backgroundColor: '#e9ecef', borderColor: 'rgba(27,31,35,.35)', boxShadow: 'inset 0 0.15em 0.3em rgba(27,31,35,.15)' } }} > View Raw </a> </span> </ContentAreaHeaderBar> {highlights ? ( <CodeListing highlights={highlights} /> ) : uri ? ( <ImageViewer path={path} uri={uri} /> ) : ( <BinaryViewer /> )} </ContentArea> ); } if (process.env.NODE_ENV !== 'production') { FileViewer.propTypes = { path: PropTypes.string.isRequired, details: PropTypes.shape({ contentType: PropTypes.string.isRequired, highlights: PropTypes.arrayOf(PropTypes.string), // code uri: PropTypes.string, // images integrity: PropTypes.string.isRequired, language: PropTypes.string.isRequired, size: PropTypes.number.isRequired }).isRequired }; }