/** @jsx jsx */ import React from 'react'; import PropTypes from 'prop-types'; import { jsx } from '@emotion/core'; import formatBytes from 'pretty-bytes'; import sortBy from 'sort-by'; function getDirname(name) { return ( name .split('/') .slice(0, -1) .join('/') || '.' ); } function getMatchingEntries(entry, entries) { const dirname = entry.name || '.'; return Object.keys(entries) .filter(name => entry.name !== name && getDirname(name) === dirname) .map(name => entries[name]); } function getRelativeName(base, name) { return base.length ? name.substr(base.length + 1) : name; } const styles = { tableHead: { textAlign: 'left', padding: '0.5em 1em' }, tableCell: { padding: '0.5em 1em' }, evenRow: { backgroundColor: '#eee' } }; export default function DirectoryListing({ filename, entry, entries }) { const rows = []; if (filename !== '/') { rows.push( <tr key=".."> <td css={styles.tableCell}> <a title="Parent directory" href="../"> .. </a> </td> <td css={styles.tableCell}>-</td> <td css={styles.tableCell}>-</td> <td css={styles.tableCell}>-</td> </tr> ); } const matchingEntries = getMatchingEntries(entry, entries); matchingEntries .filter(({ type }) => type === 'directory') .sort(sortBy('name')) .forEach(({ name }) => { const relName = getRelativeName(entry.name, name); const href = relName + '/'; rows.push( <tr key={name}> <td css={styles.tableCell}> <a title={relName} href={href}> {href} </a> </td> <td css={styles.tableCell}>-</td> <td css={styles.tableCell}>-</td> <td css={styles.tableCell}>-</td> </tr> ); }); matchingEntries .filter(({ type }) => type === 'file') .sort(sortBy('name')) .forEach(({ name, size, contentType, lastModified }) => { const relName = getRelativeName(entry.name, name); rows.push( <tr key={name}> <td css={styles.tableCell}> <a title={relName} href={relName}> {relName} </a> </td> <td css={styles.tableCell}>{contentType}</td> <td css={styles.tableCell}>{formatBytes(size)}</td> <td css={styles.tableCell}>{lastModified}</td> </tr> ); }); return ( <div> <table css={{ width: '100%', borderCollapse: 'collapse', font: '0.85em Monaco, monospace' }} > <thead> <tr> <th css={styles.tableHead}>Name</th> <th css={styles.tableHead}>Type</th> <th css={styles.tableHead}>Size</th> <th css={styles.tableHead}>Last Modified</th> </tr> </thead> <tbody> {rows.map((row, index) => React.cloneElement(row, { style: index % 2 ? undefined : styles.evenRow }) )} </tbody> </table> </div> ); } if (process.env.NODE_ENV !== 'production') { const entryType = PropTypes.shape({ name: PropTypes.string.isRequired }); DirectoryListing.propTypes = { filename: PropTypes.string.isRequired, entry: entryType.isRequired, entries: PropTypes.objectOf(entryType).isRequired }; }