├── .gitignore
├── .travis.yml
├── README.md
├── example
├── big.csv
├── example.csv
├── example.gif
├── index.css
├── index.js
└── transactions.csv
├── index.html
├── index.js
├── lib
├── tbody.js
└── update.js
├── package.json
└── test.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - '4'
4 | before_script:
5 | - export DISPLAY=:99.0; sh -e /etc/init.d/xvfb start
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # csv-viewer [](https://travis-ci.org/shama/csv-viewer)
2 |
3 | A WIP CSV viewer element.
4 |
5 | 
6 |
7 | ## Installing/Running
8 |
9 | ```shell
10 | git clone git://github.com/shama/csv-viewer && cd csv-viewer
11 | npm i
12 | npm start
13 | ```
14 |
15 | Visit `http://localhost:9966`
16 |
17 | ## Example
18 |
19 | [https://shama.github.io/csv-viewer](https://shama.github.io/csv-viewer)
20 |
21 | ## Usage
22 |
23 | ```js
24 | var viewer = require('csv-viewer')
25 |
26 | // Get some CSV data
27 | var csv = [
28 | ['Name', 'Address', 'Phone'],
29 | ['Grizzly', '123 Fake St', '707-123-4567'],
30 | ]
31 |
32 | // Build the element and attach to page
33 | var element = viewer(csv)
34 | document.body.appendChild(element)
35 | ```
36 |
--------------------------------------------------------------------------------
/example/example.csv:
--------------------------------------------------------------------------------
1 | "Name","Address","Phone"
2 | "Max","123 Real St","415-123-4567"
3 | "Karissa","123 Some St","000-123-4567"
4 | "Mathias","123 Dat St","800-123-4567"
5 | "Kyle","123 Fake St","707-123-4567"
6 |
--------------------------------------------------------------------------------
/example/example.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shama/csv-viewer/ebec1d61bf8df3172770cda1b267c20ccf47d23a/example/example.gif
--------------------------------------------------------------------------------
/example/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-style: normal;
3 | font-weight: 400;
4 | font-size: 14px;
5 | background-color: #ecf0f1;
6 | color: #2c3e50;
7 | font-family: 'Lucida Grande', Helvetica, Arial;
8 | margin: 0;
9 | box-sizing: border-box;
10 | }
11 | button {
12 | background: transparent;
13 | border: none;
14 | cursor: pointer;
15 | }
16 | button:hover {
17 | color: #CFD8DC;
18 | }
19 | button:focus {
20 | outline: none;
21 | }
22 |
23 | nav {
24 | background-color: #2980b9;
25 | width: 100%;
26 | padding: 1em;
27 | margin-bottom: 1em;
28 | color: #ecf0f1;
29 | }
30 | nav h3 {
31 | width: auto;
32 | float: left;
33 | margin: 0;
34 | padding-right: 1em;
35 | line-height: 1em;
36 | }
37 | nav button {
38 | text-decoration: underline;
39 | padding: 0 1em;
40 | color: #ecf0f1;
41 | }
42 |
43 | .contents {
44 | width: 90%;
45 | margin: 0 auto;
46 | }
47 |
--------------------------------------------------------------------------------
/example/index.js:
--------------------------------------------------------------------------------
1 | var csvViewer = require('../index.js')
2 | var yo = require('yo-yo')
3 | var createRouter = require('base-router')
4 | var parseCSV = require('babyparse').parse
5 | var nets = require('nets')
6 | var update = require('../lib/update.js')
7 |
8 | // Create a route that loads our model
9 | var router = createRouter({
10 | '/:file': function (params, done) {
11 | nets({
12 | url: 'example/' + (params.file || 'example.csv')
13 | }, function (err, res, csv) {
14 | csv = parseCSV(csv.toString())
15 | // TODO: Check for errors in csv
16 | done(err, csv)
17 | })
18 | }
19 | }, { location: 'hash' })
20 |
21 | // Create a loading state
22 | var loading = yo`
Loading files....
`
23 | router.on('loading', function () {
24 | update('.app', render(loading))
25 | })
26 |
27 | // On successful transitions, render the app
28 | router.on('transition', function (router, csv) {
29 | var rows = csv.data
30 | update('.app', render(csvViewer(rows), rows.length))
31 | })
32 |
33 | // Main application
34 | function render (contents, total) {
35 | var nav = [
36 | 'example.csv',
37 | 'big.csv',
38 | 'transactions.csv'
39 | ]
40 | return yo`
41 |
49 |
50 | ${contents}
51 |
Total Rows: ${total || '?'}
52 |
53 |
`
54 | }
55 |
56 | // Initial render
57 | var app = render(loading)
58 | document.body.appendChild(app)
59 |
60 | // Start by going to example.csv
61 | router.transitionTo('/')
62 |
--------------------------------------------------------------------------------
/example/transactions.csv:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shama/csv-viewer/ebec1d61bf8df3172770cda1b267c20ccf47d23a/example/transactions.csv
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | var yo = require('yo-yo')
2 | var csjs = require('csjs')
3 | var update = require('./lib/update.js')
4 | var tbody = require('./lib/tbody.js')
5 |
6 | module.exports = function csvViewer (data, opts) {
7 | console.time('csvViewer')
8 | var headerRow = data.splice(0, 1)[0]
9 | var asc = true
10 | var sortByIndex = 0
11 | var element = render(data)
12 | console.timeEnd('csvViewer')
13 | return element
14 |
15 | function render (data) {
16 | return yo`
17 | ${thead(headerRow)}
18 | ${tbody(data)}
19 |
`
20 | }
21 |
22 | function thead (row) {
23 | return yo`
24 |
25 | ${row.map(function (col, idx) {
26 | var icon = ''
27 | if (idx === sortByIndex) {
28 | icon = (asc) ? 'fa-caret-down' : 'fa-caret-up'
29 | icon = yo`
`
30 | }
31 | return yo`
32 |
35 |
`
36 | })}
37 |
38 |
`
39 | }
40 |
41 | function sort (idx) {
42 | asc = !asc
43 | sortByIndex = idx
44 | data = data.sort(function (a, b) {
45 | var x = a[sortByIndex] || ''
46 | var y = b[sortByIndex] || ''
47 | return (asc) ? x.localeCompare(y) : y.localeCompare(x)
48 | })
49 | update('.' + className, render(data))
50 | }
51 | }
52 |
53 | var styles = module.exports.styles = csjs`
54 | .csv-viewer {
55 | table-layout: fixed;
56 | border-collapse: collapse;
57 | width: 100%;
58 | }
59 | .row {
60 | display: flex;
61 | flex-wrap: wrap;
62 | }
63 | .th {
64 | flex: 1;
65 | background-color: #27ae60;
66 | border: 1px solid #27ae60;
67 | padding: .5em;
68 | text-overflow: ellipsis;
69 | overflow: hidden;
70 | }
71 | .csv-viewer .tr:nth-child(even) {
72 | background-color: #F5F5F5;
73 | }
74 | `
75 | var className = styles['csv-viewer']
76 |
--------------------------------------------------------------------------------
/lib/tbody.js:
--------------------------------------------------------------------------------
1 | var objectAssign = require('object-assign')
2 | var yo = require('yo-yo')
3 | var csjs = require('csjs')
4 | var domcss = require('dom-css')
5 | var update = require('./update.js')
6 |
7 | module.exports = function tbody (rows, opts) {
8 | console.time('tbody')
9 | opts = objectAssign({
10 | height: 500,
11 | rowHeight: 30
12 | }, opts)
13 | var scrollTop = 0
14 | var visibleStart = 0
15 | var visibleEnd = 0
16 | var displayStart = 0
17 | var displayEnd = 0
18 | var scrollTop = 0
19 | var element = render(partialRows(rows, scrollTop))
20 | console.timeEnd('tbody')
21 | return element
22 |
23 | function render (rows) {
24 | var tbody = yo`
25 | ${toprow()}
26 | ${rows.map(function (row) {
27 | if (!row || row.length < 1 || row[0].length <= 1) return ''
28 | return yo`
29 | ${row.map(function (col) {
30 | return yo`
${col}
`
31 | })}
32 |
`
33 | })}
34 | ${bottomrow()}
35 |
`
36 | return tbody
37 | }
38 |
39 | function onscroll () {
40 | var section = partialRows(rows, this.scrollTop)
41 | update('.' + styles.tbody, render(section))
42 | }
43 |
44 | function toprow () {
45 | var row = yo``
46 | domcss(row, 'height', displayStart * opts.rowHeight)
47 | return row
48 | }
49 |
50 | function bottomrow () {
51 | var row = yo``
52 | domcss(row, 'height', (rows.length - displayEnd) * opts.rowHeight)
53 | return row
54 | }
55 |
56 | function partialRows (rows, scrollTop) {
57 | var total = rows.length
58 | var rowsPerBody = Math.floor((opts.height - 2) / opts.rowHeight)
59 | visibleStart = Math.round(Math.floor(scrollTop / opts.rowHeight))
60 | visibleEnd = Math.round(Math.min(visibleStart + rowsPerBody))
61 | displayStart = Math.round(Math.max(0, Math.floor(scrollTop / opts.rowHeight) - rowsPerBody * 1.5))
62 | displayEnd = Math.round(Math.min(displayStart + 4 * rowsPerBody, total))
63 | return rows.slice(displayStart, displayEnd)
64 | }
65 | }
66 |
67 | var styles = module.exports.styles = csjs`
68 | .tbody {
69 | overflow: auto;
70 | height: 500px;
71 | }
72 | .row {
73 | display: flex;
74 | flex-wrap: wrap;
75 | }
76 | .td {
77 | flex: 1;
78 | border: 1px solid #27ae60;
79 | padding: .5em;
80 | text-overflow: ellipsis;
81 | overflow: hidden;
82 | }
83 | .row:nth-child(even) {
84 | background-color: #F5F5F5;
85 | }
86 | `
87 |
--------------------------------------------------------------------------------
/lib/update.js:
--------------------------------------------------------------------------------
1 | var document = require('global/document')
2 | var yo = require('yo-yo')
3 |
4 | module.exports = function update (f, t) {
5 | if (typeof f === 'string') {
6 | f = document.querySelector(f)
7 | }
8 | yo.update(f, t)
9 | }
10 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "csv-viewer",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "wzrd example/index.js:bundle.js -- -t csjs-injectify",
8 | "test": "standard && browserify test.js | testron",
9 | "build": "browserify example/index.js -o bundle.js",
10 | "deploy": "gh-pages-deploy"
11 | },
12 | "author": "Kyle Robinson Young (http://dontkry.com)",
13 | "license": "MIT",
14 | "dependencies": {
15 | "babyparse": "^0.4.3",
16 | "dom-css": "^2.0.0",
17 | "global": "^4.3.0",
18 | "normalize.css": "^3.0.3",
19 | "object-assign": "^4.0.1",
20 | "yo-yo": "^1.1.1"
21 | },
22 | "devDependencies": {
23 | "base-router": "^1.1.0",
24 | "browserify": "^13.0.0",
25 | "csjs": "^1.0.0",
26 | "csjs-injectify": "^1.0.0",
27 | "dat-browserify": "git+https://github.com/karissa/dat-browserify.git",
28 | "electron-prebuilt": "^0.36.9",
29 | "font-awesome": "^4.5.0",
30 | "gh-pages-deploy": "^0.4.0",
31 | "nets": "^3.2.0",
32 | "normalize.css": "^3.0.3",
33 | "standard": "^6.0.7",
34 | "tape": "^4.4.0",
35 | "testron": "^1.2.0",
36 | "wzrd": "^1.3.1",
37 | "yo-yoify": "^1.0.2"
38 | },
39 | "browserify": {
40 | "transform": ["yo-yoify"]
41 | },
42 | "gh-pages-deploy": {
43 | "prep": [
44 | "build"
45 | ],
46 | "noprompt": false
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/test.js:
--------------------------------------------------------------------------------
1 | var test = require('tape')
2 | var explorer = require('./index.js')
3 |
4 | test('basically works', function (t) {
5 | t.plan(2)
6 | var files = [
7 | {
8 | path: 'bears',
9 | type: 'folder',
10 | mtime: new Date(),
11 | children: [
12 | {
13 | path: 'bears/grizzly.js',
14 | type: 'file',
15 | mtime: new Date()
16 | }
17 | ]
18 | }
19 | ]
20 | var element = explorer(files)
21 | var treeButton = element.querySelector('.fs-explorer-tree button')
22 | t.equal(treeButton.textContent, 'bears', 'tree should display the folder name')
23 | t.equal(treeButton.className, 'folder', 'tree should have the class folder on folders')
24 | t.end()
25 | })
26 |
--------------------------------------------------------------------------------