sheetforce
| @ -6,7 +6,7 @@ sidebar_custom_props: | ||||
| 
 | ||||
| import current from '/version.js'; | ||||
| 
 | ||||
| # AMD | ||||
| # AMD (define) | ||||
| 
 | ||||
| Each standalone release script is available at <https://cdn.sheetjs.com/>. | ||||
| 
 | ||||
| @ -72,6 +72,8 @@ require(['./xlsx.full.min'], function(XLSX) { | ||||
| }); | ||||
| ``` | ||||
| 
 | ||||
| #### Aliases | ||||
| 
 | ||||
| The `requirejs.config` function can define aliases through the `paths` key: | ||||
| 
 | ||||
| ```js | ||||
| @ -81,3 +83,11 @@ requirejs.config({ | ||||
|   } | ||||
| }); | ||||
| ``` | ||||
| 
 | ||||
| Once that is set, app code can freely require `xlsx`: | ||||
| 
 | ||||
| ```js | ||||
| require(['xlsx'], function(XLSX) { | ||||
|   // ... use XLSX here | ||||
| }); | ||||
| ``` | ||||
|  | ||||
							
								
								
									
										378
									
								
								docz/docs/04-getting-started/03-demos/05-salesforce.md
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						| @ -0,0 +1,378 @@ | ||||
| --- | ||||
| sidebar_position: 6 | ||||
| --- | ||||
| 
 | ||||
| # Salesforce LWC | ||||
| 
 | ||||
| Salesforce apps can use third-party libraries in "Lightning Web Components". | ||||
| 
 | ||||
| This demo assumes familiarity with Lightning Web Components.  Salesforce has a | ||||
| [detailed introduction.](https://developer.salesforce.com/docs/component-library/documentation/en/lwc/lwc.get_started_introduction) | ||||
| 
 | ||||
| :::caution | ||||
| 
 | ||||
| Some of the details may differ across releases of Salesforce. This demo is based | ||||
| on Lightning API version `55.0` and was last tested on 2022 June 26. | ||||
| 
 | ||||
| Salesforce may change the platform in backwards-incompatible ways, so the demo | ||||
| may require some adjustments.  The official documentation should be consulted. | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| ## Getting Started | ||||
| 
 | ||||
| This demo was built on a "Developer Edition" account. At the time of writing, an | ||||
| [account can be created for free.](https://developer.salesforce.com/signup) | ||||
| 
 | ||||
| ### Create Sample Project and Component | ||||
| 
 | ||||
| Following the steps in ["Develop in Non-Scratch Orgs"](https://developer.salesforce.com/docs/component-library/documentation/en/lwc/lwc.get_started_sfdx_deploy): | ||||
| 
 | ||||
| ```bash | ||||
| ## Login | ||||
| sfdx force:auth:web:login -d -a LWC-Hub | ||||
| 
 | ||||
| ## Create Sample Project and Component | ||||
| sfdx force:project:create --projectname SheetForce | ||||
| cd SheetForce | ||||
| sfdx force:lightning:component:create --type lwc -n sheetComponent -d force-app/main/default/lwc | ||||
| ``` | ||||
| 
 | ||||
| By default, the component will not be available to app pages.  A few files must | ||||
| be changed: | ||||
| 
 | ||||
| `force-app\main\default\lwc\sheetComponent\sheetComponent.html` add some HTML: | ||||
| 
 | ||||
| ```html force-app\main\default\lwc\sheetComponent\sheetComponent.html | ||||
| <template> | ||||
|   <!-- highlight-next-line --> | ||||
|   <b>SheetForce demo</b> | ||||
| </template> | ||||
| ``` | ||||
| 
 | ||||
| `force-app\main\default\lwc\sheetComponent\sheetComponent.js-meta.xml` change | ||||
| `isExposed` from `false` to `true` and add some metadata: | ||||
| 
 | ||||
| ```xml | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata"> | ||||
|   <apiVersion>55.0</apiVersion> | ||||
|   <!-- highlight-start --> | ||||
|   <isExposed>true</isExposed> | ||||
|   <masterLabel>SheetForce</masterLabel> | ||||
|   <description>SheetJS Demo</description> | ||||
|   <targets> | ||||
|     <target>lightning__AppPage</target> | ||||
|   </targets> | ||||
|   <!-- highlight-end --> | ||||
| </LightningComponentBundle> | ||||
| ``` | ||||
| 
 | ||||
| ### Deploy Sample Project | ||||
| 
 | ||||
| Deploy the project: | ||||
| 
 | ||||
| ```bash | ||||
| sfdx force:source:deploy -p force-app -u SALESFORCE@USER.NAME # replace with actual username | ||||
| ``` | ||||
| 
 | ||||
| The custom component can be found in Custom Code > Lightning Components. | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| ### Initialize App Page | ||||
| 
 | ||||
| Create an "App Page" in the "Lightning App Builder".  Instructions are included | ||||
| in [Hello World in a Scratch Org](https://developer.salesforce.com/docs/component-library/documentation/en/lwc/lwc.get_started_sfdx_hello_world) | ||||
| 
 | ||||
| The following options should be set: | ||||
| - The "App Page" option should be selected. | ||||
| - The App Label should be set to "SheetJS Demo". | ||||
| - The "One Region" layout should be selected. | ||||
| 
 | ||||
| Under Custom components, you should see "SheetForce".  Click and drag it into | ||||
| the app builder main view to add it to the page. | ||||
| 
 | ||||
| Click "Save" and click "Yes" to activate.  The following options should be set: | ||||
| - Click "Change..." next to "Icon" and pick a memorable icon | ||||
| - Under "Lightning Experience" click "LightningBolt" then "Add page to app" | ||||
| 
 | ||||
| Click "Save" to activate the page, then click the left arrow to return to Setup. | ||||
| 
 | ||||
| Click the App Launcher and select "Bolt Solutions" then "SheetJS Demo".  You | ||||
| should see a page like | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| 
 | ||||
| ## Adding the Standalone Script | ||||
| 
 | ||||
| The [standalone script](../../installation/standalone) can be downloaded and | ||||
| added as a static resource.  Due to Salesforce naming restrictions, it will have | ||||
| to be renamed to `sheetjs.js` when adding the static resource. | ||||
| 
 | ||||
| 1) Download <https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js> | ||||
| 
 | ||||
| :::warning | ||||
| 
 | ||||
| **DO NOT "COPY AND PASTE"!**  The file should be explicitly downloaded.  Copying | ||||
| and pasting corrupts the source code and the component will fail in subtle ways. | ||||
| 
 | ||||
| The easiest approach is to right-click the link and select "Save Link As..." | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| 2) Move the file to the `force-app/main/default/staticresources/` folder and | ||||
|    rename the file to `sheetjs.js`. | ||||
| 
 | ||||
| 3) Create `force-app/main/default/staticresources/sheetjs.resource-meta.xml`: | ||||
| 
 | ||||
| ```xml | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <StaticResource xmlns="http://soap.sforce.com/2006/04/metadata"> | ||||
|   <cacheControl>Private</cacheControl> | ||||
|   <contentType>application/javascript</contentType> | ||||
| </StaticResource> | ||||
| ``` | ||||
| 
 | ||||
| 4) Deploy the project again: | ||||
| 
 | ||||
| ```bash | ||||
| sfdx force:source:deploy -p force-app -u SALESFORCE@USER.NAME # replace with actual username | ||||
| ``` | ||||
| 
 | ||||
| :::note | ||||
| 
 | ||||
| The official documentation recommends adding a static resource with a ZIP file. | ||||
| That approach is not explored in this demo. | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| Custom Code > Static Resources should now list `sheetjs`: | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| ### Test the Static Resource | ||||
| 
 | ||||
| The script can be loaded from component code with: | ||||
| 
 | ||||
| ```js | ||||
| import XLSX from '@salesforce/resourceUrl/sheetjs'; | ||||
| ``` | ||||
| 
 | ||||
| The library includes a version number that can be displayed: | ||||
| 
 | ||||
| 1) Add a reference in `sheetComponent.js` and expose the `version` property: | ||||
| 
 | ||||
| ```js | ||||
| import { LightningElement } from 'lwc'; | ||||
| import { loadScript } from 'lightning/platformResourceLoader'; | ||||
| // highlight-next-line | ||||
| import sheetjs from '@salesforce/resourceUrl/sheetjs'; | ||||
| 
 | ||||
| export default class SheetComponent extends LightningElement { | ||||
|   version = "???"; // start with ??? | ||||
|   async connectedCallback() { | ||||
|     // highlight-next-line | ||||
|     await loadScript(this, sheetjs); // load the library | ||||
|     // At this point, the library is accessible with the `XLSX` variable | ||||
|     this.version = XLSX.version; | ||||
|   } | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| 2) Reference the variable in `sheetComponent.html`: | ||||
| 
 | ||||
| ```html | ||||
| <template> | ||||
|   <!-- highlight-next-line --> | ||||
|   <b>SheetForce {version}</b> | ||||
| </template> | ||||
| ``` | ||||
| 
 | ||||
| 3) Deploy the project again and re-load the Bolt Solutions "SheetJS Demo" page: | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| ## Exporting Data from SF Lists | ||||
| 
 | ||||
| :::note | ||||
| 
 | ||||
| There are many different data types and APIs.  This demo uses the deprecated | ||||
| `getListUi` function to pull account data. | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| ### Steps | ||||
| 
 | ||||
| #### Getting Account Data | ||||
| 
 | ||||
| The main method to obtain data is `getListUi` and the key for account data is | ||||
| `ACCOUNT_OBJECT`: | ||||
| 
 | ||||
| ```js | ||||
| import { getListUi } from 'lightning/uiListApi'; | ||||
| import ACCOUNT_OBJECT from '@salesforce/schema/Account'; | ||||
| 
 | ||||
| // ... | ||||
| 
 | ||||
| export default class SheetComponent extends LightningElement { | ||||
|   @wire(getListUi, { | ||||
|     objectApiName: ACCOUNT_OBJECT.objectApiName, | ||||
|     listViewApiName: 'AllAccounts' | ||||
|   }) listInfo({ error, data }) { | ||||
| 
 | ||||
|     // LIST DATA AVAILABLE HERE | ||||
| 
 | ||||
|   }; | ||||
|   // ... | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| #### Generating an Array of Arrays | ||||
| 
 | ||||
| SheetJS most reliably translates "arrays of arrays", a nested array which | ||||
| directly maps to individual cell addresses.  For example: | ||||
| 
 | ||||
| ```js | ||||
| var data = [ | ||||
|   ["Name",      "Phone"],           // row 1 | ||||
|   ["Foo Bar",   "(555) 555-5555"],  // row 2 | ||||
|   ["Baz Qux",   "(555) 555-5556"]   // row 3 | ||||
| ]; | ||||
| ``` | ||||
| 
 | ||||
| The APIs typically return nested objects, so the array must be constructed. | ||||
| 
 | ||||
| <details><summary><b>Salesforce Representation</b> (click to show)</summary> | ||||
| 
 | ||||
| The `data` parameter in the callback has a deep structure. Typically one would | ||||
| set a property in the component and display data in a template: | ||||
| 
 | ||||
| ```js | ||||
|   // ... | ||||
|   // declare records variable in the component | ||||
|   records; | ||||
|   @wire(getListUi, { | ||||
|     objectApiName: ACCOUNT_OBJECT.objectApiName, | ||||
|     listViewApiName: 'AllAccounts' | ||||
|   }) listInfo({ error, data }) { | ||||
|     if (data) { | ||||
|       // data.records.records is the array of interest | ||||
|       this.records = data.records.records; | ||||
|       this.error = undefined; | ||||
|     } | ||||
|   } | ||||
|   // ... | ||||
| ``` | ||||
| 
 | ||||
| The template itself would iterate across the records: | ||||
| 
 | ||||
| ```html | ||||
| <template> | ||||
|   <template if:true={records}> | ||||
|     <table> | ||||
|       <tr><th>Name</th><th>Phone</th></tr> | ||||
|       <template for:each={records} for:item="record"> | ||||
|         <tr key={record.fields.Id.value}> | ||||
|           <td>{record.fields.Name.value}</td> | ||||
|           <td>{record.fields.Phone.value}</td> | ||||
|         </tr> | ||||
|       </template> | ||||
|     </table> | ||||
|   </template> | ||||
| </template> | ||||
| ``` | ||||
| 
 | ||||
| </details> | ||||
| 
 | ||||
| A suitable SheetJS array of arrays can be constructed by mapping across records: | ||||
| 
 | ||||
| ```js | ||||
|       var headers = [ "Name", "Phone" ]; | ||||
|       this.aoa = [headers].concat(data.records.records.map(record => [ | ||||
|         record.fields.Name.value,  // Name field | ||||
|         record.fields.Phone.value, // Phone field | ||||
|       ])); | ||||
| ``` | ||||
| 
 | ||||
| This is readily exported to a spreadsheet in a callback function: | ||||
| 
 | ||||
| ```js | ||||
|   @api async download() { | ||||
|     await loadScript(this, sheetjs); // load the library | ||||
|     // create workbook | ||||
|     var wb = XLSX.utils.book_new(); | ||||
|     var ws = XLSX.utils.aoa_to_sheet(this.aoa); | ||||
|     XLSX.utils.book_append_sheet(wb, ws, "Data"); | ||||
|     // export | ||||
|     XLSX.writeFile(wb, "SheetForceExport.xlsx"); | ||||
|   }; | ||||
| ``` | ||||
| 
 | ||||
| ### Complete Example | ||||
| 
 | ||||
| 1) Add a button to `sheetComponent.html` that will call a `download` callback: | ||||
| 
 | ||||
| ```html | ||||
| <template> | ||||
|   <!-- if the `aoa` property is set, show a button --> | ||||
|   <template if:true={aoa}> | ||||
|     <button onclick={download}><b>Click to Export!</b></button> | ||||
|   </template> | ||||
|   <!-- if the `aoa` property is not set, show a message --> | ||||
|   <template if:false={aoa}><b>Please wait for data to load ...</b></template> | ||||
| </template> | ||||
| ``` | ||||
| 
 | ||||
| 2) Replace `sheetComponent.js` with the following: | ||||
| 
 | ||||
| ```js | ||||
| import { LightningElement, wire, api } from 'lwc'; | ||||
| import { loadScript } from 'lightning/platformResourceLoader'; | ||||
| import { getListUi } from 'lightning/uiListApi'; | ||||
| import ACCOUNT_OBJECT from '@salesforce/schema/Account'; | ||||
| 
 | ||||
| import sheetjs from '@salesforce/resourceUrl/sheetjs'; | ||||
| 
 | ||||
| export default class SheetComponent extends LightningElement { | ||||
|   aoa; // will hold data for export | ||||
|   @wire(getListUi, { | ||||
|     objectApiName: ACCOUNT_OBJECT.objectApiName, | ||||
|     listViewApiName: 'AllAccounts' | ||||
|   }) listInfo({ error, data }) { | ||||
|     if (data) { | ||||
|       var headers = [ "Name", "Phone" ]; | ||||
|       // create AOA and assign to `aoa` property | ||||
|       this.aoa = [headers].concat(data.records.records.map(record => [ | ||||
|         record.fields.Name.value,  // Name field | ||||
|         record.fields.Phone.value, // Phone field | ||||
|       ])); | ||||
|     } else if (error) console.log(error); | ||||
|   }; | ||||
|   @api async download() { | ||||
|     await loadScript(this, sheetjs); // load the library | ||||
|     // create workbook | ||||
|     var wb = XLSX.utils.book_new(); | ||||
|     var ws = XLSX.utils.aoa_to_sheet(this.aoa); | ||||
|     XLSX.utils.book_append_sheet(wb, ws, "Data"); | ||||
|     // export | ||||
|     XLSX.writeFile(wb, "SheetForceExport.xlsx"); | ||||
|   }; | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| 3) Re-deploy and refresh the app page: | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| The simple export has all of the data: | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| :::note | ||||
| 
 | ||||
| [SheetJS Pro](https://sheetjs.com/pro) offers additional styling options like | ||||
| cell styling, automatic column width calculations, and frozen rows. | ||||
| 
 | ||||
| ::: | ||||
| @ -35,6 +35,7 @@ The demo projects include small runnable examples and short explainers. | ||||
| - [`Google Sheets API`](./gsheet) | ||||
| - [`ExtendScript for Adobe Apps`](./extendscript) | ||||
| - [`NetSuite SuiteScript`](./netsuite) | ||||
| - [`SalesForce Lightning Web Components`](./salesforce) | ||||
| - [`Excel JavaScript API`](./excel) | ||||
| - [`Headless Browsers`](https://github.com/SheetJS/SheetJS/tree/master/demos/headless/) | ||||
| - [`Other JavaScript Engines`](https://github.com/SheetJS/SheetJS/tree/master/demos/altjs/) | ||||
|  | ||||
| Before Width: | Height: | Size: 25 KiB | 
| Before Width: | Height: | Size: 27 KiB | 
| @ -54,6 +54,9 @@ The [demos](../getting-started/demos) cover special deployments in more detail. | ||||
| `XLSX.readFile` supports reading local files in platforms like NodeJS. In other | ||||
| platforms like React Native, `XLSX.read` should be called with file data. | ||||
| 
 | ||||
| In-browser processing where users drag-and-drop files or use a file element are | ||||
| covered in [the "User Submissions" example.](#example-user-submissions) | ||||
| 
 | ||||
| <Tabs> | ||||
|   <TabItem value="nodejs" label="NodeJS"> | ||||
| 
 | ||||
| @ -180,16 +183,33 @@ The [`extendscript` demo](../getting-started/demos/extendscript) includes a more | ||||
| 
 | ||||
| ### Example: User Submissions | ||||
| 
 | ||||
| <details> | ||||
|   <summary><b>User-submitted file in a web page ("Drag-and-Drop")</b> (click to show)</summary> | ||||
| This example focuses on user-submitted files through a drag-and-drop event, HTML | ||||
| file input element, or network request. | ||||
| 
 | ||||
| <Tabs> | ||||
|   <TabItem value="browser" label="Browser"> | ||||
| 
 | ||||
| **For modern websites targeting Chrome 76+**, `File#arrayBuffer` is recommended: | ||||
| 
 | ||||
| <Tabs> | ||||
|   <TabItem value="dnd" label="Drag and Drop"> | ||||
| 
 | ||||
| Assume `drop_dom_element` is the DOM element that will listen for changes: | ||||
| 
 | ||||
| ```html | ||||
| <div id="drop_dom_element">Drop files here</div> | ||||
| ``` | ||||
| 
 | ||||
| The event property is `e.dataTransfer`.  The code snippet highlights the | ||||
| difference between the drag-and-drop example and the file input example: | ||||
| 
 | ||||
| For modern websites targeting Chrome 76+, `File#arrayBuffer` is recommended: | ||||
| 
 | ||||
| ```js | ||||
| // XLSX is a global from the standalone script | ||||
| 
 | ||||
| async function handleDropAsync(e) { | ||||
|   e.stopPropagation(); e.preventDefault(); | ||||
|   // highlight-next-line | ||||
|   const f = e.dataTransfer.files[0]; | ||||
|   /* f is a File */ | ||||
|   const data = await f.arrayBuffer(); | ||||
| @ -201,11 +221,57 @@ async function handleDropAsync(e) { | ||||
| drop_dom_element.addEventListener("drop", handleDropAsync, false); | ||||
| ``` | ||||
| 
 | ||||
| For maximal compatibility, the `FileReader` API should be used: | ||||
|   </TabItem> | ||||
|   <TabItem value="file" label="HTML File Input Element"> | ||||
| 
 | ||||
| Starting with an HTML INPUT element with `type="file"`: | ||||
| 
 | ||||
| ```html | ||||
| <input type="file" id="input_dom_element"> | ||||
| ``` | ||||
| 
 | ||||
| The event property is `e.target`.  The code snippet highlights the difference | ||||
| between the drag-and-drop example and the file input example: | ||||
| 
 | ||||
| ```js | ||||
| // XLSX is a global from the standalone script | ||||
| 
 | ||||
| async function handleFileAsync(e) { | ||||
|   // highlight-next-line | ||||
|   const file = e.target.files[0]; | ||||
|   const data = await file.arrayBuffer(); | ||||
|   /* data is an ArrayBuffer */ | ||||
|   const workbook = XLSX.read(data); | ||||
| 
 | ||||
|   /* DO SOMETHING WITH workbook HERE */ | ||||
| } | ||||
| input_dom_element.addEventListener("change", handleFileAsync, false); | ||||
| ``` | ||||
| 
 | ||||
|   </TabItem> | ||||
| </Tabs> | ||||
| 
 | ||||
| <https://oss.sheetjs.com/sheetjs/> demonstrates the FileReader technique. | ||||
| 
 | ||||
| 
 | ||||
| **For maximal compatibility (IE10+)**, the `FileReader` approach is recommended: | ||||
| 
 | ||||
| <Tabs> | ||||
|   <TabItem value="dnd" label="Drag and Drop"> | ||||
| 
 | ||||
| Assume `drop_dom_element` is the DOM element that will listen for changes: | ||||
| 
 | ||||
| ```html | ||||
| <div id="drop_dom_element">Drop files here</div> | ||||
| ``` | ||||
| 
 | ||||
| The event property is `e.dataTransfer`.  The code snippet highlights the | ||||
| difference between the drag-and-drop example and the file input example: | ||||
| 
 | ||||
| ```js | ||||
| function handleDrop(e) { | ||||
|   e.stopPropagation(); e.preventDefault(); | ||||
|   // highlight-next-line | ||||
|   var f = e.dataTransfer.files[0]; | ||||
|   /* f is a File */ | ||||
|   var reader = new FileReader(); | ||||
| @ -221,12 +287,8 @@ function handleDrop(e) { | ||||
| drop_dom_element.addEventListener("drop", handleDrop, false); | ||||
| ``` | ||||
| 
 | ||||
| <https://oss.sheetjs.com/sheetjs/> demonstrates the FileReader technique. | ||||
| 
 | ||||
| </details> | ||||
| 
 | ||||
| <details> | ||||
|   <summary><b>User-submitted file with an HTML INPUT element</b> (click to show)</summary> | ||||
|   </TabItem> | ||||
|   <TabItem value="file" label="HTML File Input Element"> | ||||
| 
 | ||||
| Starting with an HTML INPUT element with `type="file"`: | ||||
| 
 | ||||
| @ -234,26 +296,12 @@ Starting with an HTML INPUT element with `type="file"`: | ||||
| <input type="file" id="input_dom_element"> | ||||
| ``` | ||||
| 
 | ||||
| For modern websites targeting Chrome 76+, `Blob#arrayBuffer` is recommended: | ||||
| 
 | ||||
| ```js | ||||
| // XLSX is a global from the standalone script | ||||
| 
 | ||||
| async function handleFileAsync(e) { | ||||
|   const file = e.target.files[0]; | ||||
|   const data = await file.arrayBuffer(); | ||||
|   /* data is an ArrayBuffer */ | ||||
|   const workbook = XLSX.read(data); | ||||
| 
 | ||||
|   /* DO SOMETHING WITH workbook HERE */ | ||||
| } | ||||
| input_dom_element.addEventListener("change", handleFileAsync, false); | ||||
| ``` | ||||
| 
 | ||||
| For broader support (including IE10+), the `FileReader` approach is recommended: | ||||
| The event property is `e.target`.  The code snippet highlights the difference | ||||
| between the drag-and-drop example and the file input example: | ||||
| 
 | ||||
| ```js | ||||
| function handleFile(e) { | ||||
|   // highlight-next-line | ||||
|   var file = e.target.files[0]; | ||||
|   var reader = new FileReader(); | ||||
|   reader.onload = function(e) { | ||||
| @ -268,13 +316,13 @@ function handleFile(e) { | ||||
| input_dom_element.addEventListener("change", handleFile, false); | ||||
| ``` | ||||
| 
 | ||||
|   </TabItem> | ||||
| </Tabs> | ||||
| 
 | ||||
| The [`oldie` demo](https://github.com/SheetJS/SheetJS/tree/master/demos/oldie/) shows an IE-compatible fallback scenario. | ||||
| 
 | ||||
| </details> | ||||
| 
 | ||||
| 
 | ||||
| <details> | ||||
|   <summary><b>NodeJS Server File Uploads</b> (click to show)</summary> | ||||
|   </TabItem> | ||||
|   <TabItem value="nodejs" label="NodeJS"> | ||||
| 
 | ||||
| `read` can accept a NodeJS buffer.  `readFile` can read files generated by a | ||||
| HTTP POST request body parser like [`formidable`](https://npm.im/formidable): | ||||
| @ -299,7 +347,64 @@ const server = http.createServer((req, res) => { | ||||
| 
 | ||||
| The [`server` demo](https://github.com/SheetJS/SheetJS/tree/master/demos/server) has more advanced examples. | ||||
| 
 | ||||
| </details> | ||||
|   </TabItem> | ||||
|   <TabItem value="deno" label="Deno"> | ||||
| 
 | ||||
| [Drash](https://drash.land/drash/) is a framework for Deno's HTTP server.  In a | ||||
| `POST` request handler, the body parser can pull file data into a `Uint8Array`: | ||||
| 
 | ||||
| <pre><code parentName="pre" {...{"className": "language-ts"}}>{`\ | ||||
| // @deno-types="https://cdn.sheetjs.com/xlsx-${current}/package/types/index.d.ts" | ||||
| import * as XLSX from 'https://cdn.sheetjs.com/xlsx-${current}/package/xlsx.mjs'; | ||||
| /* load the codepage support library for extended support with older formats  */ | ||||
| import * as cptable from 'https://cdn.sheetjs.com/xlsx-${current}/package/dist/cpexcel.full.mjs'; | ||||
| XLSX.set_cptable(cptable); | ||||
| 
 | ||||
| import * as Drash from "https://deno.land/x/drash@v2.5.4/mod.ts"; | ||||
| 
 | ||||
| class SheetResource extends Drash.Resource { | ||||
|   public paths = ["/"]; | ||||
| 
 | ||||
|   public POST(request: Drash.Request, response: Drash.Response) { | ||||
|     // highlight-next-line | ||||
|     const file = request.bodyParam<Drash.Types.BodyFile>("file"); | ||||
|     if (!file) throw new Error("File is required!"); | ||||
|     // highlight-next-line | ||||
|     var wb = XLSX.read(file.content, {type: "buffer"}); | ||||
|     var html = XLSX.utils.sheet_to_html(wb.Sheets[wb.SheetNames[0]]); | ||||
|     return response.html(html); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| const server = new Drash.Server({ hostname: "", port: 7262, protocol: "http", | ||||
|   resources: [ | ||||
|     // highlight-next-line | ||||
|     SheetResource, | ||||
|   ], | ||||
| }); | ||||
| 
 | ||||
| server.run();`}</code></pre> | ||||
| 
 | ||||
| :::note | ||||
| 
 | ||||
| Deno must be run with the `--allow-net` flag to enable network requests: | ||||
| 
 | ||||
| ```bash | ||||
| $ deno run --allow-net test-server.ts | ||||
| ``` | ||||
| 
 | ||||
| To test, submit a POST request to http://localhost:7262 including a file: | ||||
| 
 | ||||
| ```bash | ||||
| curl -X POST -F "file=@test.xlsx" http://localhost:7262/ | ||||
| ``` | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| 
 | ||||
|   </TabItem> | ||||
| </Tabs> | ||||
| 
 | ||||
| 
 | ||||
| ### Example: Remote File | ||||
| 
 | ||||
| @ -355,6 +460,7 @@ Node 17.5 and 18.0 have native support for fetch: | ||||
| ```js | ||||
| const XLSX = require("xlsx"); | ||||
| 
 | ||||
| const url = "http://oss.sheetjs.com/test_files/formula_stress_test.xlsx"; | ||||
| const data = await (await fetch(url)).arrayBuffer(); | ||||
| /* data is an ArrayBuffer */ | ||||
| const workbook = XLSX.read(data); | ||||
| @ -368,6 +474,7 @@ For broader compatibility, third-party modules are recommended. | ||||
| var XLSX = require("xlsx"); | ||||
| var request = require("request"); | ||||
| 
 | ||||
| var url = "http://oss.sheetjs.com/test_files/formula_stress_test.xlsx"; | ||||
| request({url: url, encoding: null}, function(err, resp, body) { | ||||
|   var workbook = XLSX.read(body); | ||||
| 
 | ||||
| @ -381,6 +488,7 @@ request({url: url, encoding: null}, function(err, resp, body) { | ||||
| const XLSX = require("xlsx"); | ||||
| const axios = require("axios"); | ||||
| 
 | ||||
| const url = "http://oss.sheetjs.com/test_files/formula_stress_test.xlsx"; | ||||
| (async() => { | ||||
|   const res = await axios.get(url, {responseType: "arraybuffer"}); | ||||
|   /* res.data is a Buffer */ | ||||
| @ -390,6 +498,34 @@ const axios = require("axios"); | ||||
| })(); | ||||
| ``` | ||||
| 
 | ||||
|   </TabItem> | ||||
|   <TabItem value="deno" label="Deno"> | ||||
| 
 | ||||
| Deno has native support for fetch. | ||||
| 
 | ||||
| <pre><code parentName="pre" {...{"className": "language-ts"}}>{`\ | ||||
| // @deno-types="https://cdn.sheetjs.com/xlsx-${current}/package/types/index.d.ts" | ||||
| import * as XLSX from 'https://cdn.sheetjs.com/xlsx-${current}/package/xlsx.mjs'; | ||||
| /* load the codepage support library for extended support with older formats  */ | ||||
| import * as cptable from 'https://cdn.sheetjs.com/xlsx-${current}/package/dist/cpexcel.full.mjs'; | ||||
| XLSX.set_cptable(cptable); | ||||
| 
 | ||||
| const url = "http://oss.sheetjs.com/test_files/formula_stress_test.xlsx"; | ||||
| // highlight-next-line | ||||
| const data = await (await fetch(url)).arrayBuffer(); | ||||
| /* data is an ArrayBuffer */ | ||||
| const workbook = XLSX.read(data);`}</code></pre> | ||||
| 
 | ||||
| :::note | ||||
| 
 | ||||
| Deno must be run with the `--allow-net` flag to enable network requests: | ||||
| 
 | ||||
| ``` | ||||
| $ deno run --allow-net test-fetch.ts | ||||
| ``` | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
|   </TabItem> | ||||
|   <TabItem value="electron" label="Electron"> | ||||
| 
 | ||||
| @ -400,6 +536,7 @@ resources.  Responses should be manually concatenated using `Buffer.concat`: | ||||
| const XLSX = require("xlsx"); | ||||
| const { net } = require("electron"); | ||||
| 
 | ||||
| const url = "http://oss.sheetjs.com/test_files/formula_stress_test.xlsx"; | ||||
| const req = net.request(url); | ||||
| req.on("response", (res) => { | ||||
|   const bufs = []; // this array will collect all of the buffers | ||||
|  | ||||
| @ -62,9 +62,6 @@ rest of the worksheet structure. | ||||
| 
 | ||||
| #### Examples | ||||
| 
 | ||||
| <details> | ||||
|   <summary><b>Add a new worksheet to a workbook</b> (click to show)</summary> | ||||
| 
 | ||||
| This example uses [`XLSX.utils.aoa_to_sheet`](../api/utilities#array-of-arrays-input). | ||||
| 
 | ||||
| ```js | ||||
| @ -81,8 +78,6 @@ var ws = XLSX.utils.aoa_to_sheet(ws_data); | ||||
| XLSX.utils.book_append_sheet(wb, ws, ws_name); | ||||
| ``` | ||||
| 
 | ||||
| </details> | ||||
| 
 | ||||
| ## Modifying Cell Values | ||||
| 
 | ||||
| #### API | ||||
| @ -117,9 +112,6 @@ function and the optional `opts` argument in more detail. | ||||
| 
 | ||||
| #### Examples | ||||
| 
 | ||||
| <details> | ||||
|   <summary><b>Appending rows to a worksheet</b> (click to show)</summary> | ||||
| 
 | ||||
| The special origin value `-1` instructs `sheet_add_aoa` to start in column A of | ||||
| the row after the last row in the range, appending the data: | ||||
| 
 | ||||
| @ -130,8 +122,6 @@ XLSX.utils.sheet_add_aoa(worksheet, [ | ||||
| ], { origin: -1 }); | ||||
| ``` | ||||
| 
 | ||||
| </details> | ||||
| 
 | ||||
| ## Modifying Other Worksheet / Workbook / Cell Properties | ||||
| 
 | ||||
| The ["Common Spreadsheet Format"](../csf/general) section describes | ||||
|  | ||||
| @ -1,10 +1,24 @@ | ||||
| # Spreadsheet Features | ||||
| 
 | ||||
| import DocCardList from '@theme/DocCardList'; | ||||
| import {useCurrentSidebarCategory} from '@docusaurus/theme-common'; | ||||
| 
 | ||||
| Even for basic features like date storage, the official Excel formats store the | ||||
| same content in different ways.  The parsers are expected to convert from the | ||||
| underlying file format representation to the Common Spreadsheet Format.  Writers | ||||
| are expected to convert from CSF back to the underlying file format. | ||||
| 
 | ||||
| The following topics are covered in sub-pages: | ||||
| 
 | ||||
| <ul>{useCurrentSidebarCategory().items.map((item, index) => { | ||||
|   const listyle = (item.customProps?.icon) ? { | ||||
|     listStyleImage: `url("${item.customProps.icon}")` | ||||
|   } : {}; | ||||
|   return (<li style={listyle} {...(item.customProps?.class ? {className: item.customProps.class}: {})}> | ||||
|     <a href={item.href}>{item.label}</a>{item.customProps?.summary && (" - " + item.customProps.summary)} | ||||
|   </li>); | ||||
| })}</ul> | ||||
| 
 | ||||
| ## Row and Column Properties | ||||
| 
 | ||||
| <details> | ||||
| @ -133,12 +147,7 @@ The format can either be specified as a string or as an index into the format | ||||
| table.  Parsers are expected to populate `workbook.SSF` with the number format | ||||
| table.  Writers are expected to serialize the table. | ||||
| 
 | ||||
| Custom tools should ensure that the local table has each used format string | ||||
| somewhere in the table.  Excel convention mandates that the custom formats start | ||||
| at index 164.  The following example creates a custom format from scratch: | ||||
| 
 | ||||
| <details> | ||||
|   <summary><b>New worksheet with custom format</b> (click to show)</summary> | ||||
| The following example creates a custom format from scratch: | ||||
| 
 | ||||
| ```js | ||||
| var wb = { | ||||
| @ -154,8 +163,6 @@ var wb = { | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| </details> | ||||
| 
 | ||||
| The rules are slightly different from how Excel displays custom number formats. | ||||
| In particular, literal characters must be wrapped in double quotes or preceded | ||||
| by a backslash. For more info, see the Excel documentation article | ||||
| @ -371,32 +378,51 @@ supported in `XLSM`, `XLSB`, and `BIFF8 XLS` formats.  The supported format | ||||
| writers automatically insert the data blobs if it is present in the workbook and | ||||
| associate with the worksheet names. | ||||
| 
 | ||||
| <details> | ||||
| 	<summary><b>Custom Code Names</b> (click to show)</summary> | ||||
| The `vbaraw` property stores raw bytes. [SheetJS Pro](https://sheetjs.com/pro) | ||||
| offers a special component for extracting macro text from the VBA blob, editing | ||||
| the VBA project, and exporting new VBA blobs. | ||||
| 
 | ||||
| #### Round-tripping Macro Enabled Files | ||||
| 
 | ||||
| In order to preserve macro when reading and writing files, the `bookVBA` option | ||||
| must be set to true when reading and when writing.  In addition, the output file | ||||
| format must support macros.  `XLSX` notably does not support macros, and `XLSM` | ||||
| should be used in its place: | ||||
| 
 | ||||
| ```js | ||||
| /* Reading data */ | ||||
| var wb = XLSX.read(data, { bookVBA: true }); // read file and distill VBA blob | ||||
| var vbablob = wb.vbaraw; | ||||
| ``` | ||||
| 
 | ||||
| #### Code Names | ||||
| 
 | ||||
| By default, Excel will use `ThisWorkbook` or a translation `DieseArbeitsmappe` | ||||
| for the workbook.  Each worksheet will be identified using the default `Sheet#` | ||||
| naming pattern even if the worksheet names have changed. | ||||
| 
 | ||||
| A custom workbook code name will be stored in `wb.Workbook.WBProps.CodeName`. | ||||
| For exports, assigning the property will override the default value. | ||||
| 
 | ||||
| The workbook code name is stored in `wb.Workbook.WBProps.CodeName`.  By default, | ||||
| Excel will write `ThisWorkbook` or a translated phrase like `DieseArbeitsmappe`. | ||||
| Worksheet and Chartsheet code names are in the worksheet properties object at | ||||
| `wb.Workbook.Sheets[i].CodeName`.  Macrosheets and Dialogsheets are ignored. | ||||
| 
 | ||||
| The readers and writers preserve the code names, but they have to be manually | ||||
| set when adding a VBA blob to a different workbook. | ||||
| 
 | ||||
| </details> | ||||
| 
 | ||||
| <details> | ||||
| 	<summary><b>Macrosheets</b> (click to show)</summary> | ||||
| #### Macrosheets | ||||
| 
 | ||||
| Older versions of Excel also supported a non-VBA "macrosheet" sheet type that | ||||
| stored automation commands.  These are exposed in objects with the `!type` | ||||
| property set to `"macro"`. | ||||
| 
 | ||||
| </details> | ||||
| Under the hood, Excel treats Macrosheets as normal worksheets with special | ||||
| interpretation of the function expressions. | ||||
| 
 | ||||
| <details> | ||||
| 	<summary><b>Detecting macros in workbooks</b> (click to show)</summary> | ||||
| #### Detecting Macros in Workbooks | ||||
| 
 | ||||
| The `vbaraw` field will only be set if macros are present, so testing is simple: | ||||
| The `vbaraw` field will only be set if macros are present.  Macrosheets will be | ||||
| explicitly flagged.  Combining the two checks yields a simple function: | ||||
| 
 | ||||
| ```js | ||||
| function wb_has_macro(wb/*:workbook*/)/*:boolean*/ { | ||||
| @ -406,5 +432,3 @@ function wb_has_macro(wb/*:workbook*/)/*:boolean*/ { | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| </details> | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										
											BIN
										
									
								
								docz/static/files/sfcustcomp.png
									
									
									
									
									
										Normal file
									
								
							
							
								
									
								
								
								
								
								
									
									
								
							
						
						| After Width: | Height: | Size: 81 KiB | 
							
								
								
									
										
											BIN
										
									
								
								docz/static/files/sfexport.png
									
									
									
									
									
										Normal file
									
								
							
							
								
									
								
								
								
								
								
									
									
								
							
						
						| After Width: | Height: | Size: 81 KiB | 
							
								
								
									
										
											BIN
										
									
								
								docz/static/files/sfinitial.png
									
									
									
									
									
										Normal file
									
								
							
							
								
									
								
								
								
								
								
									
									
								
							
						
						| After Width: | Height: | Size: 98 KiB | 
							
								
								
									
										
											BIN
										
									
								
								docz/static/files/sfstatic.png
									
									
									
									
									
										Normal file
									
								
							
							
								
									
								
								
								
								
								
									
									
								
							
						
						| After Width: | Height: | Size: 62 KiB | 
							
								
								
									
										
											BIN
										
									
								
								docz/static/files/sfversion.png
									
									
									
									
									
										Normal file
									
								
							
							
								
									
								
								
								
								
								
									
									
								
							
						
						| After Width: | Height: | Size: 72 KiB | 
							
								
								
									
										
											BIN
										
									
								
								docz/static/files/sfxlexport.png
									
									
									
									
									
										Normal file
									
								
							
							
								
									
								
								
								
								
								
									
									
								
							
						
						| After Width: | Height: | Size: 82 KiB |