unpkg/modules/client/autoIndex/DirectoryListing.js

143 lines
3.3 KiB
JavaScript

/** @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
};
}