From 95f65248a0f7b3c344881831067365c0aef571fb Mon Sep 17 00:00:00 2001 From: SheetJS <dev@sheetjs.com> Date: Mon, 1 Aug 2022 01:34:23 -0400 Subject: [PATCH] angularjs --- .../03-demos/10-database.md | 170 +++++++++++++++ .../04-getting-started/03-demos/12-legacy.md | 136 +++++++++++- .../04-getting-started/03-demos/14-grid.md | 153 +++++++++++++ .../docs/04-getting-started/03-demos/index.md | 5 +- docz/static/angularjs/index.html | 78 +++++++ docz/static/angularjs/ui-grid.html | 201 ++++++++++++++++++ docz/static/cdg/index.html | 167 +++++++++++++++ 7 files changed, 906 insertions(+), 4 deletions(-) create mode 100644 docz/docs/04-getting-started/03-demos/14-grid.md create mode 100644 docz/static/angularjs/index.html create mode 100644 docz/static/angularjs/ui-grid.html create mode 100644 docz/static/cdg/index.html diff --git a/docz/docs/04-getting-started/03-demos/10-database.md b/docz/docs/04-getting-started/03-demos/10-database.md index 1919cf3..0058705 100644 --- a/docz/docs/04-getting-started/03-demos/10-database.md +++ b/docz/docs/04-getting-started/03-demos/10-database.md @@ -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 diff --git a/docz/docs/04-getting-started/03-demos/12-legacy.md b/docz/docs/04-getting-started/03-demos/12-legacy.md index 830bbf6..90c87c2 100644 --- a/docz/docs/04-getting-started/03-demos/12-legacy.md +++ b/docz/docs/04-getting-started/03-demos/12-legacy.md @@ -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> \ No newline at end of file diff --git a/docz/docs/04-getting-started/03-demos/14-grid.md b/docz/docs/04-getting-started/03-demos/14-grid.md new file mode 100644 index 0000000..aa1a543 --- /dev/null +++ b/docz/docs/04-getting-started/03-demos/14-grid.md @@ -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> diff --git a/docz/docs/04-getting-started/03-demos/index.md b/docz/docs/04-getting-started/03-demos/index.md index 54d04e6..09e64bb 100644 --- a/docz/docs/04-getting-started/03-demos/index.md +++ b/docz/docs/04-getting-started/03-demos/index.md @@ -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 diff --git a/docz/static/angularjs/index.html b/docz/static/angularjs/index.html new file mode 100644 index 0000000..71ab290 --- /dev/null +++ b/docz/static/angularjs/index.html @@ -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> diff --git a/docz/static/angularjs/ui-grid.html b/docz/static/angularjs/ui-grid.html new file mode 100644 index 0000000..0a4f6b7 --- /dev/null +++ b/docz/static/angularjs/ui-grid.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> diff --git a/docz/static/cdg/index.html b/docz/static/cdg/index.html new file mode 100644 index 0000000..055c3cf --- /dev/null +++ b/docz/static/cdg/index.html @@ -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>