angularjs

This commit is contained in:
SheetJS 2022-08-01 01:34:23 -04:00
parent b71c65775b
commit 95f65248a0
7 changed files with 906 additions and 4 deletions

@ -563,6 +563,176 @@ for(var i = 0; i < localForage.length; ++i) aoo.push(JSON.parse(await localForag
const wb = XLSX.utils.json_to_sheet(aoo);
```
### Other SQL Databases
The `generate_sql` function from ["Building Schemas from Worksheets"](#building-schemas-from-worksheets)
can be adapted to generate SQL statements for a variety of databases, including:
**PostgreSQL**
The `pg` connector library was tested against the `generate_sql` output as-is.
The `rows` property of a query result is an array of objects that plays nice
with `json_to_sheet`:
```js
const aoa = await connection.query(`SELECT * FROM DataTable`).rows;
const worksheet = XLSX.utils.json_to_sheet(aoa);
```
**MySQL / MariaDB**
The `mysql2` connector library was tested. The differences are shown below,
primarily stemming from the different quoting requirements and field types.
<details><summary><b>Differences</b> (click to show)</summary>
```js
// highlight-start
// define mapping between determined types and MySQL types
const PG = { "n": "REAL", "s": "TEXT", "b": "TINYINT" };
// highlight-end
function generate_sql(ws, wsname) {
// generate an array of objects from the data
const aoo = XLSX.utils.sheet_to_json(ws);
// types will map column headers to types, while hdr holds headers in order
const types = {}, hdr = [];
// loop across each row object
aoo.forEach(row =>
// Object.entries returns a row of [key, value] pairs. Loop across those
Object.entries(row).forEach(([k,v]) => {
// If this is first time seeing key, mark unknown and append header array
if(!types[k]) { types[k] = "?"; hdr.push(k); }
// skip null and undefined
if(v == null) return;
// check and resolve type
switch(typeof v) {
case "string": // strings are the broadest type
types[k] = "s"; break;
case "number": // if column is not string, number is the broadest type
if(types[k] != "s") types[k] = "n"; break;
case "boolean": // only mark boolean if column is unknown or boolean
if("?b".includes(types[k])) types[k] = "b"; break;
default: types[k] = "s"; break; // default to string type
}
})
);
// The final array consists of the CREATE TABLE query and a series of INSERTs
return [
// generate CREATE TABLE query and return batch
// highlight-next-line
`CREATE TABLE ${wsname} (${hdr.map(h =>
// highlight-next-line
`${h} ${PG[types[h]]}`
).join(", ")});`
].concat(aoo.map(row => { // generate INSERT query for each row
// entries will be an array of [key, value] pairs for the data in the row
const entries = Object.entries(row);
// fields will hold the column names and values will hold the values
const fields = [], values = [];
// check each key/value pair in the row
entries.forEach(([k,v]) => {
// skip null / undefined
if(v == null) return;
// highlight-next-line
fields.push(`${k}`);
// when the field type is numeric, `true` -> 1 and `false` -> 0
if(types[k] == "n") values.push(typeof v == "boolean" ? (v ? 1 : 0) : v);
// otherwise,
// highlight-next-line
else values.push(`"${v.toString().replaceAll('"', '""')}"`);
})
if(fields.length) return `INSERT INTO \`${wsname}\` (${fields.join(", ")}) VALUES (${values.join(", ")})`;
})).filter(x => x); // filter out skipped rows
}
```
</details>
The first property of a query result is an array of objects that plays nice
with `json_to_sheet`:
```js
const aoa = await connection.query(`SELECT * FROM DataTable`)[0];
const worksheet = XLSX.utils.json_to_sheet(aoa);
```
### Query Builders
Query builders are designed to simplify query generation and normalize field
types and other database minutiae.
**Knex**
The result of a `SELECT` statement is an array of objects:
```js
const aoo = await connection.select("*").from("DataTable");
const worksheet = XLSX.utils.json_to_sheet(aoa);
```
Knex wraps primitive types when creating a table. `generate_sql` takes a `knex`
connection object and uses the API:
<details><summary><b>Generating a Table</b> (click to show)</summary>
```js
// define mapping between determined types and Knex types
const PG = { "n": "float", "s": "text", "b": "boolean" };
async function generate_sql(knex, ws, wsname) {
// generate an array of objects from the data
const aoo = XLSX.utils.sheet_to_json(ws);
// types will map column headers to types, while hdr holds headers in order
const types = {}, hdr = [];
// loop across each row object
aoo.forEach(row =>
// Object.entries returns a row of [key, value] pairs. Loop across those
Object.entries(row).forEach(([k,v]) => {
// If this is first time seeing key, mark unknown and append header array
if(!types[k]) { types[k] = "?"; hdr.push(k); }
// skip null and undefined
if(v == null) return;
// check and resolve type
switch(typeof v) {
case "string": // strings are the broadest type
types[k] = "s"; break;
case "number": // if column is not string, number is the broadest type
if(types[k] != "s") types[k] = "n"; break;
case "boolean": // only mark boolean if column is unknown or boolean
if("?b".includes(types[k])) types[k] = "b"; break;
default: types[k] = "s"; break; // default to string type
}
})
);
await knex.schema.dropTableIfExists(wsname);
await knex.schema.createTable(wsname, (table) => { hdr.forEach(h => { table[PG[types[h]] || "text"](h); }); });
for(let i = 0; i < aoo.length; ++i) {
if(!aoo[i] || !Object.keys(aoo[i]).length) continue;
try { await knex.insert(aoo[i]).into(wsname); } catch(e) {}
}
return knex;
}
```
</details>
### MongoDB Structured Collections

@ -20,6 +20,135 @@ designed to be referenced with `<script>` tags.
## Frameworks
### AngularJS
[AngularJS](https://en.wikipedia.org/wiki/AngularJS) was a front-end MVC
framework that was abandoned by Google in 2022. It should not be confused with
"Angular" the modern framework.
The [Live demo](pathname:///angularjs/index.html) shows a simple table that is
updated with file data and exported to spreadsheets.
This demo uses AngularJS 1.5.0.
<details><summary><b>Full Exposition</b> (click to show)</summary>
**Array of Objects**
A common data table is often stored as an array of objects:
```js
$scope.data = [
{ Name: "Bill Clinton", Index: 42 },
{ Name: "GeorgeW Bush", Index: 43 },
{ Name: "Barack Obama", Index: 44 },
{ Name: "Donald Trump", Index: 45 }
];
```
This neatly maps to a table with `ng-repeat`:
```html
<table id="sjs-table">
<tr><th>Name</th><th>Index</th></tr>
<tr ng-repeat="row in data">
<td>{{row.Name}}</td>
<td>{{row.Index}}</td>
</tr>
</table>
```
The `$http` service can request binary data using the `"arraybuffer"` response
type coupled with `XLSX.read` with type `"array"`:
```js
$http({
method:'GET',
url:'https://sheetjs.com/pres.xlsx',
responseType:'arraybuffer'
}).then(function(data) {
var wb = XLSX.read(data.data, {type:"array"});
var d = XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]);
$scope.data = d;
}, function(err) { console.log(err); });
```
The HTML table can be directly exported with `XLSX.utils.table_to_book`:
```js
var wb = XLSX.utils.table_to_book(document.getElementById('sjs-table'));
XLSX.writeFile(wb, "export.xlsx");
```
**Import Directive**
A general import directive is fairly straightforward:
- Define the `importSheetJs` directive in the app:
```js
app.directive("importSheetJs", [SheetJSImportDirective]);
```
- Add the attribute `import-sheet-js=""` to the file input element:
```html
<input type="file" import-sheet-js="" multiple="false" />
```
- Define the directive:
```js
function SheetJSImportDirective() {
return {
scope: { opts: '=' },
link: function ($scope, $elm) {
$elm.on('change', function (changeEvent) {
var reader = new FileReader();
reader.onload = function (e) {
/* read workbook */
var ab = e.target.result;
var workbook = XLSX.read(ab);
/* DO SOMETHING WITH workbook HERE */
};
reader.readAsArrayBuffer(changeEvent.target.files[0]);
});
}
};
}
```
**Export Service**
An export can be triggered at any point! Depending on how data is represented,
a workbook object can be built using the utility functions. For example, using
an array of objects:
```js
/* starting from this data */
var data = [
{ name: "Barack Obama", pres: 44 },
{ name: "Donald Trump", pres: 45 }
];
/* generate a worksheet */
var ws = XLSX.utils.json_to_sheet(data);
/* add to workbook */
var wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, "Presidents");
/* write workbook and force a download */
XLSX.writeFile(wb, "sheetjs.xlsx");
```
</details>
### KnockoutJS
[KnockoutJS](https://en.wikipedia.org/wiki/Knockout_(web_framework)) was a
@ -28,8 +157,9 @@ popular MVVM framework.
The [Live demo](pathname:///knockout/knockout.html) shows a view model that is
updated with file data and exported to spreadsheets.
<details><summary><b>Full Exposition</b> (click to show)</summary>
#### State
**State**
Arrays of arrays are the simplest data structure for representing worksheets.
@ -70,7 +200,7 @@ var aoa = model.aoa();
var ws = XLSX.utils.aoa_to_sheet(aoa);
```
#### Data Binding
**Data Binding**
`data-bind="foreach: ..."` provides a simple approach for binding to `TABLE`:
@ -92,3 +222,5 @@ binding is possible using the `$parent` and `$index` binding context properties:
</tr>
</table>
```
</details>

@ -0,0 +1,153 @@
---
sidebar_position: 14
title: Data Grids and UI
---
Various JavaScript UI components provide a more interactive editing experience.
Most are able to interchange with arrays of arrays or arrays of data objects.
This demo focuses on a few open source data grids.
:::note
[SheetJS Pro](https://sheetjs.com/pro) offers additional features like styling
and images. The UI tools typically support many of these advanced features.
To eliminate any confusion, the live examples linked from this page demonstrate
SheetJS Community Edition data interchange.
:::
## Managed Lifecycle
Many UI components tend to manage the entire lifecycle, providing methods to
import and export data.
The `sheet_to_json` utility function generates arrays of objects, which is
suitable for a number of libraries. When more advanced shapes are needed,
it is easier to munge the output of an array of arrays.
### Canvas DataGrid
After extensive testing, [`canvas-datagrid`](https://canvas-datagrid.js.org/demo.html)
stood out as a very high-performance grid with an incredibly simple API.
[Click here for a live integration demo.](pathname:///cdg/index.html)
<details><summary><b>Full Exposition</b> (click to show)</summary>
**Obtaining the Library**
The `canvas-datagrid` NodeJS packages include a minified script that can be
directly inserted as a script tag. The unpkg CDN also serves this script:
```html
<script src="https://unpkg.com/canvas-datagrid/dist/canvas-datagrid.js"></script>
```
**Previewing Data**
The HTML document needs a container element:
```html
<div id="gridctr"></div>
```
Grid initialization is a one-liner:
```js
var grid = canvasDatagrid({
parentNode: document.getElementById('gridctr'),
data: []
});
```
For large data sets, it's necessary to constrain the size of the grid.
```js
grid.style.height = '100%';
grid.style.width = '100%';
```
Once the workbook is read and the worksheet is selected, assigning the data
variable automatically updates the view:
```js
grid.data = XLSX.utils.sheet_to_json(ws, {header:1});
```
This demo previews the first worksheet.
**Editing**
`canvas-datagrid` handles the entire edit cycle. No intervention is necessary.
**Saving Data**
`grid.data` is immediately readable and can be converted back to a worksheet.
Some versions return an array-like object without the length, so a little bit of
preparation may be needed:
```js
/* converts an array of array-like objects into an array of arrays */
function prep(arr) {
var out = [];
for(var i = 0; i < arr.length; ++i) {
if(!arr[i]) continue;
if(Array.isArray(arr[i])) { out[i] = arr[i]; continue };
var o = new Array();
Object.keys(arr[i]).forEach(function(k) { o[+k] = arr[i][k] });
out[i] = o;
}
return out;
}
/* build worksheet from the grid data */
var ws = XLSX.utils.aoa_to_sheet(prep(grid.data));
/* build up workbook */
var wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, 'SheetJS');
/* generate download */
XLSX.writeFile(wb, "SheetJS.xlsx");
```
**Additional Features**
This demo barely scratches the surface. The underlying grid component includes
many additional features including massive data streaming, sorting and styling.
</details>
### Angular UI Grid
:::warning
This UI Grid is for AngularJS, not the modern Angular. New projects should not
use AngularJS. This demo is included for legacy applications.
The [AngularJS demo](./legacy#angularjs) covers more general strategies.
:::
[Click here for a live integration demo.](pathname:///angularjs/ui-grid.html)
<details><summary><b>Notes</b> (click to show)</summary>
The library does not provide any way to modify the import button, so the demo
includes a simple directive for a HTML File Input control. It also includes a
sample service for export which adds an item to the export menu.
The demo `SheetJSImportDirective` follows the prescription from the README for
File input controls using `readAsArrayBuffer`, converting to a suitable
representation and updating the scope.
`SheetJSExportService` exposes export functions for `XLSB` and `XLSX`. Other
file formats can be exported by changing the `bookType` variable. It grabs
values from the grid, builds an array of arrays, generates a workbook and forces
a download. By setting the `filename` and `sheetname` options in the `ui-grid`
options, the output can be controlled.
</details>

@ -18,7 +18,7 @@ The demo projects include small runnable examples and short explainers.
### Frameworks
- [`Angular.JS`](https://github.com/SheetJS/SheetJS/tree/master/demos/angular/)
- [`Angular.JS`](./legacy#angularjs)
- [`Angular 2+ and Ionic`](https://github.com/SheetJS/SheetJS/tree/master/demos/angular2/)
- [`Knockout`](./legacy#knockout)
- [`Meteor`](https://github.com/SheetJS/SheetJS/tree/master/demos/meteor/)
@ -27,10 +27,11 @@ The demo projects include small runnable examples and short explainers.
### Front-End UI Components
- [`canvas-datagrid`](https://github.com/SheetJS/SheetJS/tree/master/demos/datagrid/)
- [`canvas-datagrid`](./grid#canvas-datagrid)
- [`x-spreadsheet`](https://github.com/SheetJS/SheetJS/tree/master/demos/xspreadsheet/)
- [`react-data-grid`](https://github.com/SheetJS/SheetJS/tree/master/demos/react/modify/)
- [`vue3-table-light`](https://github.com/SheetJS/SheetJS/tree/master/demos/vue/modify/)
- [`angular-ui-grid`](./grid#angular-ui-grid)
### Platforms and Integrations

@ -0,0 +1,78 @@
<!DOCTYPE html>
<!-- sheetjs (C) 2013-present SheetJS http://sheetjs.com -->
<!-- vim: set ts=2: -->
<html ng-app="s5s">
<head>
<title>SheetJS + AngularJS</title>
<!-- Angular -->
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.min.js"></script>
<!-- SheetJS library -->
<script src="https://cdn.sheetjs.com/xlsx-latest/package/dist/shim.min.js"></script>
<script src="https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js"></script>
</head>
<body>
<pre>
<b><a href="http://sheetjs.com">SheetJS + AngularJS demo</a></b>
The core library can be used as-is in AngularJS applications.
The <a href="https://docs.sheetjs.com">Community Edition README</a> details some common use cases.
We also have some <a href="http://sheetjs.com/demos/">more public demos</a>
This demo shows:
- $http request for XLSX file and scope update with data
- HTML table using ng-repeat
- XLSX table export using `XLSX.utils.table_to_book`
<a href="https://sheetjs.com/pres.xlsx">Sample Spreadsheet</a>
The table has hardcoded fields `Name` and `Index`.
</pre>
<div ng-controller="sheetjs">
<table id="s5s-table">
<tr><th>Name</th><th>Index</th></tr>
<tr ng-repeat="row in data">
<td>{{row.Name}}</td>
<td>{{row.Index}}</td>
</tr>
</table>
<button id="exportbtn">Export Table</button>
</div>
<script>
var app = angular.module('s5s', []);
app.controller('sheetjs', function($scope, $http) {
$http({
method:'GET',
url:'https://sheetjs.com/pres.xlsx',
responseType:'arraybuffer'
}).then(function(data) {
var wb = XLSX.read(data.data, {type:"array"});
var d = XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]);
$scope.data = d;
}, function(err) { console.log(err); });
});
exportbtn.addEventListener('click', function() {
var wb = XLSX.utils.table_to_book(document.getElementById('s5s-table'));
XLSX.writeFile(wb, "export.xlsx");
});
</script>
<script type="text/javascript">
/* eslint no-use-before-define:0 */
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-36810333-1']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
</body>
</html>

@ -0,0 +1,201 @@
<!DOCTYPE html>
<!-- SheetJS (C) 2013-present SheetJS http://sheetjs.com -->
<!-- vim: set ts=2: -->
<html ng-app="app">
<head>
<title>SheetJS + AngularJS + ui-grid</title>
<!-- Angular -->
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular-touch.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular-animate.js"></script>
<!-- SheetJS library -->
<script src="https://cdn.sheetjs.com/xlsx-latest/package/dist/shim.min.js"></script>
<script src="https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js"></script>
<!-- ui-grid -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-grid/4.11.0/ui-grid.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-grid/4.11.0/ui-grid.css"/>
<style>
.grid1 {
width: 500px;
height: 400px;
};
</style>
</head>
<body>
<pre>
<b><a href="http://sheetjs.com">SheetJS + AngularJS demo</a></b>
The core library can be used as-is in AngularJS applications.
The <a href="https://docs.sheetjs.com">Community Edition README</a> details some common use cases.
We also have some <a href="http://sheetjs.com/demos/">more public demos</a>
This demo shows:
- SheetJSExportService: a service for exporting data from a ui-grid
- SheetJSImportDirective: a directive providing a file input button for import
<a href="https://obamawhitehouse.archives.gov/sites/default/files/omb/budget/fy2014/assets/receipts.xls">Sample Spreadsheet</a>
</pre>
<div ng-controller="MainCtrl">
<input type="file" import-sheet-js="" opts="gridOptions" multiple="false" />
<div id="grid1" ui-grid="gridOptions" ui-grid-selection ui-grid-exporter class="grid"></div>
</div>
<!-- SheetJS Service -->
<script>
function SheetJSExportService(uiGridExporterService) {
function exportSheetJS(gridApi, wopts) {
var columns = uiGridExporterService.getColumnHeaders(gridApi.grid, 'all');
var data = uiGridExporterService.getData(gridApi.grid, 'all', 'all');
var fileName = gridApi.grid.options.filename || 'SheetJS';
fileName += wopts.bookType ? "." + wopts.bookType : '.xlsx';
var sheetName = gridApi.grid.options.sheetname || 'Sheet1';
var wb = XLSX.utils.book_new(), ws = uigrid_to_sheet(data, columns);
XLSX.utils.book_append_sheet(wb, ws, sheetName);
XLSX.writeFile(wb, fileName);
}
var service = {};
service.exportXLSB = function exportXLSB(gridApi) { return exportSheetJS(gridApi, { bookType: 'xlsb', bookSST: true, type: 'array' }); };
service.exportXLSX = function exportXLSX(gridApi) { return exportSheetJS(gridApi, { bookType: 'xlsx', bookSST: true, type: 'array' }); }
return service;
/* utilities */
function uigrid_to_sheet(data, columns) {
var o = [], oo = [], i = 0, j = 0;
/* column headers */
for(j = 0; j < columns.length; ++j) oo.push(get_value(columns[j]));
o.push(oo);
/* table data */
for(i = 0; i < data.length; ++i) {
oo = [];
for(j = 0; j < data[i].length; ++j) oo.push(get_value(data[i][j]));
o.push(oo);
}
/* aoa_to_sheet converts an array of arrays into a worksheet object */
return XLSX.utils.aoa_to_sheet(o);
}
function get_value(col) {
if(!col) return col;
if(col.value) return col.value;
if(col.displayName) return col.displayName;
if(col.name) return col.name;
return null;
}
}
function SheetJSImportDirective() {
return {
scope: { opts: '=' },
link: function($scope, $elm) {
$elm.on('change', function(changeEvent) {
var reader = new FileReader();
reader.onload = function(e) {
/* read workbook */
var ab = e.target.result;
var wb = XLSX.read(ab);
/* grab first sheet */
var wsname = wb.SheetNames[0];
var ws = wb.Sheets[wsname];
/* grab first row and generate column headers */
var aoa = XLSX.utils.sheet_to_json(ws, {header:1, raw:false});
var cols = [];
for(var i = 0; i < aoa[0].length; ++i) cols[i] = { field: aoa[0][i] };
/* generate rest of the data */
var data = [];
for(var r = 1; r < aoa.length; ++r) {
data[r-1] = {};
for(i = 0; i < aoa[r].length; ++i) {
if(aoa[r][i] == null) continue;
data[r-1][aoa[0][i]] = aoa[r][i];
}
}
/* update scope */
$scope.$apply(function() {
$scope.opts.columnDefs = cols;
$scope.opts.data = data;
});
};
reader.readAsArrayBuffer(changeEvent.target.files[0]);
});
}
};
}
</script>
<!-- App -->
<script>
var app = angular.module('app', ['ngAnimate', 'ngTouch', 'ui.grid', 'ui.grid.selection', 'ui.grid.exporter']);
/* Inject SheetJSExportService */
app.factory('SheetJSExportService', SheetJSExportService);
SheetJSExportService.inject = ['uiGridExporterService'];
app.controller('MainCtrl', ['$scope', '$http','SheetJSExportService', function($scope, $http, SheetJSExportService) {
$scope.gridOptions = {
columnDefs: [
{ field: 'name' },
{ field: 'gender', visible: false},
{ field: 'company' }
],
enableGridMenu: true,
enableSelectAll: true,
exporterMenuPdf: false,
exporterMenuCsv: false,
showHeader: true,
onRegisterApi: function(gridApi){
$scope.gridApi = gridApi;
},
/* SheetJS Service setup */
filename: "SheetJSAngular",
sheetname: "ng-SheetJS",
gridMenuCustomItems: [
{
title: 'Export all data as XLSX',
action: function() { SheetJSExportService.exportXLSX($scope.gridApi); },
order: 200
},
{
title: 'Export all data as XLSB',
action: function() { SheetJSExportService.exportXLSB($scope.gridApi); },
order: 201
}
]
};
$http.get('https://cdn.rawgit.com/angular-ui/ui-grid.info/gh-pages/data/100.json').success(function(data) { $scope.gridOptions.data = data; });
}]);
app.directive("importSheetJs", [SheetJSImportDirective]);
</script>
<script type="text/javascript">
/* eslint no-use-before-define:0 */
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-36810333-1']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
</body>
</html>

167
docz/static/cdg/index.html Normal file

@ -0,0 +1,167 @@
<!DOCTYPE html>
<!-- xlsx.js (C) 2013-present SheetJS http://sheetjs.com -->
<!-- vim: set ts=2: -->
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>SheetJS + canvas-datagrid Live Demo</title>
<style>
#drop{
border:2px dashed #bbb;
-moz-border-radius:5px;
-webkit-border-radius:5px;
border-radius:5px;
padding:25px;
text-align:center;
font:20pt bold,"Vollkorn";color:#bbb
}
#b64data{
width:100%;
}
a { text-decoration: none }
</style>
</head>
<body>
<pre>
<b><a href="http://sheetjs.com">SheetJS Data Preview Live Demo</a></b>
<a href="https://canvas-datagrid.js.org/">canvas-datagrid component library</a>
<a href="https://github.com/SheetJS/sheetjs">Source Code Repo</a>
<a href="https://github.com/SheetJS/sheetjs/issues">Issues? Something look weird? Click here and report an issue</a>
<div id="drop">Drop a spreadsheet file here to see sheet data</div>
<input type="file" name="xlfile" id="xlf" /> ... or click here to select a file
<textarea id="b64data">... or paste a base64-encoding here</textarea>
<b>Advanced Demo Options:</b>
</pre>
<p><input type="submit" value="Export to XLSX!" id="xport" onclick="export_xlsx();" disabled="true"></p>
<div id="htmlout"></div>
<br />
<script src="https://unpkg.com/canvas-datagrid/dist/canvas-datagrid.js"></script>
<script src="https://cdn.sheetjs.com/xlsx-latest/package/dist/shim.min.js"></script>
<script src="https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js"></script>
<script>
/*jshint browser:true */
/* eslint-env browser */
/* eslint no-use-before-define:0 */
/*global Uint8Array, Uint16Array, ArrayBuffer */
/*global XLSX */
var cDg;
var process_wb = (function() {
var XPORT = document.getElementById('xport');
var HTMLOUT = document.getElementById('htmlout');
return function process_wb(wb) {
/* get data */
var ws = wb.Sheets[wb.SheetNames[0]];
var data = XLSX.utils.sheet_to_json(ws, {header:1});
/* update canvas-datagrid */
if(!cDg) cDg = canvasDatagrid({ parentNode:HTMLOUT, data:data });
cDg.style.height = '100%';
cDg.style.width = '100%';
cDg.data = data;
XPORT.disabled = false;
/* create schema (for A,B,C column headings) */
var range = XLSX.utils.decode_range(ws['!ref']);
for(var i = range.s.c; i <= range.e.c; ++i) cDg.schema[i - range.s.c].title = XLSX.utils.encode_col(i);
HTMLOUT.style.height = (window.innerHeight - 400) + "px";
HTMLOUT.style.width = (window.innerWidth - 50) + "px";
if(typeof console !== 'undefined') console.log("output", new Date());
};
})();
var do_file = (function() {
return function do_file(files) {
var f = files[0];
var reader = new FileReader();
reader.onload = function(e) {
if(typeof console !== 'undefined') console.log("onload", new Date());
var data = e.target.result;
data = new Uint8Array(data);
process_wb(XLSX.read(data, {type: 'array'}));
};
reader.readAsArrayBuffer(f);
};
})();
(function() {
var drop = document.getElementById('drop');
if(!drop.addEventListener) return;
function handleDrop(e) {
e.stopPropagation();
e.preventDefault();
do_file(e.dataTransfer.files);
}
function handleDragover(e) {
e.stopPropagation();
e.preventDefault();
e.dataTransfer.dropEffect = 'copy';
}
drop.addEventListener('dragenter', handleDragover, false);
drop.addEventListener('dragover', handleDragover, false);
drop.addEventListener('drop', handleDrop, false);
})();
(function() {
var xlf = document.getElementById('xlf');
if(!xlf.addEventListener) return;
function handleFile(e) { do_file(e.target.files); }
xlf.addEventListener('change', handleFile, false);
})();
var export_xlsx = (function() {
function prep(arr) {
var out = [];
for(var i = 0; i < arr.length; ++i) {
if(!arr[i]) continue;
if(Array.isArray(arr[i])) { out[i] = arr[i]; continue };
var o = new Array();
Object.keys(arr[i]).forEach(function(k) { o[+k] = arr[i][k] });
out[i] = o;
}
return out;
}
return function export_xlsx() {
if(!cDg) return;
/* convert canvas-datagrid data to worksheet */
var new_ws = XLSX.utils.aoa_to_sheet(prep(cDg.data));
/* build workbook */
var new_wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(new_wb, new_ws, 'SheetJS');
/* write file and trigger a download */
XLSX.writeFile(new_wb, 'SheetJSCanvasDataGridExport.xlsx', {bookSST:true});
};
})();
(async() => {
const ab = await(await fetch("https://sheetjs.com/pres.numbers")).arrayBuffer();
process_wb(XLSX.read(ab));
})();
</script>
<script type="text/javascript">
/* eslint no-use-before-define:0 */
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-36810333-1']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
</body>
</html>