forked from sheetjs/docs.sheetjs.com
		
	react-native mobile refresh
This commit is contained in:
		
							parent
							
								
									9230a968c3
								
							
						
					
					
						commit
						ad27e3a947
					
				| @ -46,11 +46,11 @@ This demo was tested in the following environments: | ||||
| 
 | ||||
| | Version   | Platform | Date       | | ||||
| |:----------|:---------|:-----------| | ||||
| | `0.19.47` | NodeJS   | 2023-10-18 | | ||||
| | `0.20.16` | Browser  | 2023-12-04 | | ||||
| | `0.20.19` | NodeJS   | 2023-10-18 | | ||||
| | `0.21.6`  | NodeJS   | 2023-10-18 | | ||||
| | `6.14.2`  | NodeJS   | 2023-12-04 | | ||||
| | `0.19.47` | NodeJS   | 2024-03-31 | | ||||
| | `0.20.16` | Browser  | 2024-03-31 | | ||||
| | `0.20.19` | NodeJS   | 2024-03-31 | | ||||
| | `0.21.6`  | NodeJS   | 2024-03-31 | | ||||
| | `6.14.3`  | NodeJS   | 2024-03-31 | | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| @ -203,7 +203,7 @@ npm init -y | ||||
| 1) Install the dependencies: | ||||
| 
 | ||||
| <CodeBlock language="bash">{`\ | ||||
| npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz systemjs@6.14.2`} | ||||
| npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz systemjs@6.14.3`} | ||||
| </CodeBlock> | ||||
| 
 | ||||
| 2) Download [`SheetJSystem.js`](pathname:///systemjs/SheetJSystem.js) and move | ||||
| @ -215,7 +215,7 @@ curl -LO https://docs.sheetjs.com/systemjs/SheetJSystem.js | ||||
| 
 | ||||
| :::info pass | ||||
| 
 | ||||
| The script is handles both old-style and new-style SystemJS loaders. | ||||
| The script handles old-style and new-style SystemJS loaders. | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
|  | ||||
| @ -2,7 +2,7 @@ | ||||
| title: Sheets in PST Mailboxes | ||||
| sidebar_label: PST Mailboxes | ||||
| pagination_prev: demos/net/server/index | ||||
| pagination_next: demos/net/headless | ||||
| pagination_next: demos/net/headless/index | ||||
| --- | ||||
| 
 | ||||
| import current from '/version.js'; | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| --- | ||||
| title: Electronic Mail | ||||
| pagination_prev: demos/net/server/index | ||||
| pagination_next: demos/net/headless | ||||
| pagination_next: demos/net/headless/index | ||||
| --- | ||||
| 
 | ||||
| import current from '/version.js'; | ||||
|  | ||||
							
								
								
									
										4
									
								
								docz/docs/03-demos/03-net/08-headless/_category_.json
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										4
									
								
								docz/docs/03-demos/03-net/08-headless/_category_.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,4 @@ | ||||
| { | ||||
|   "label": "Browser Automation", | ||||
|   "position": 8 | ||||
| } | ||||
| @ -1,6 +1,7 @@ | ||||
| --- | ||||
| title: Browser Automation | ||||
| pagination_prev: demos/net/email/index | ||||
| pagination_next: demos/net/dom | ||||
| --- | ||||
| 
 | ||||
| import current from '/version.js'; | ||||
| @ -406,7 +407,7 @@ This demo was tested in the following environments: | ||||
| |:-------------|:----------|:-----------| | ||||
| | `darwin-x64` | `2.1.1`   | 2024-03-15 | | ||||
| | `win10-x64`  | `2.1.1`   | 2024-03-24 | | ||||
| 
 | ||||
| | `linux-x64`  | `2.1.1`   | 2024-03-29 | | ||||
| ::: | ||||
| 
 | ||||
| 1) [Download and extract PhantomJS](https://phantomjs.org/download.html) | ||||
| @ -425,4 +426,30 @@ will be placed in `phantomjs-2.1.1-macosx/bin/` and the command will be: | ||||
| When the script finishes, the file `SheetJSPhantomJS.xlsb` will be created. | ||||
| This file can be opened with Excel. | ||||
| 
 | ||||
| :::caution pass | ||||
| 
 | ||||
| When this demo was last tested on Linux, there were multiple errors. | ||||
| 
 | ||||
| ``` | ||||
| This application failed to start because it could not find or load the Qt platform plugin "xcb". | ||||
| ``` | ||||
| 
 | ||||
| The environment variable `QT_QPA_PLATFORM=phantom` resolves the issue. There is | ||||
| a different error after assignment: | ||||
| 
 | ||||
| ``` | ||||
| 140412268664640:error:25066067:DSO support routines:DLFCN_LOAD:could not load the shared library:dso_dlfcn.c:185:filename(libproviders.so): libproviders.so: cannot open shared object file: No such file or directory | ||||
| 140412268664640:error:25070067:DSO support routines:DSO_load:could not load the shared library:dso_lib.c:244: | ||||
| 140412268664640:error:0E07506E:configuration file routines:MODULE_LOAD_DSO:error loading dso:conf_mod.c:285:module=providers, path=providers | ||||
| 140412268664640:error:0E076071:configuration file routines:MODULE_RUN:unknown module name:conf_mod.c:222:module=providers | ||||
| ``` | ||||
| 
 | ||||
| This error is resolved by ignoring SSL errors. The complete command is: | ||||
| 
 | ||||
| ```bash | ||||
| env OPENSSL_CONF=/dev/null QT_QPA_PLATFORM=phantom ./phantomjs-2.1.1-linux-x86_64/bin/phantomjs --ignore-ssl-errors=true test.js | ||||
| ``` | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| </details> | ||||
| @ -1,5 +1,6 @@ | ||||
| --- | ||||
| title: Synthetic DOM | ||||
| pagination_prev: demos/net/headless/index | ||||
| --- | ||||
| 
 | ||||
| import current from '/version.js'; | ||||
|  | ||||
| @ -141,7 +141,7 @@ Consider the following worksheet: | ||||
| Assuming the file name is `pres.xlsx` and the data is stored in "Sheet1", the | ||||
| following nodes will be created: | ||||
| 
 | ||||
| ```js | ||||
| ```js title="GraphQL Nodes" | ||||
| [ | ||||
|   { Name: "Bill Clinton", Index: 42, type: "PresXlsxSheet1" }, | ||||
|   { Name: "GeorgeW Bush", Index: 43, type: "PresXlsxSheet1" }, | ||||
| @ -155,7 +155,7 @@ The type is a proper casing of the file name concatenated with the sheet name. | ||||
| 
 | ||||
| The following query pulls the `Name` and `Index` fields from each row: | ||||
| 
 | ||||
| ```graphql | ||||
| ```graphql title="GraphQL Query to pull Name and Index fields from each row" | ||||
| { | ||||
|   allPresXlsxSheet1 { # "all" followed by type | ||||
|     edges { | ||||
| @ -172,8 +172,12 @@ The following query pulls the `Name` and `Index` fields from each row: | ||||
| 
 | ||||
| :::note Tested Deployments | ||||
| 
 | ||||
| This demo was tested on 2023 December 04 against `create-gatsby@3.12.3`. The | ||||
| generated project used `gatsby@5.12.11` and `react@18.2.0`. | ||||
| This demo was tested in the following environments: | ||||
| 
 | ||||
| | GatsbyJS | Date       | | ||||
| |:---------|:-----------| | ||||
| | `5.12.1` | 2023-12-04 | | ||||
| | `4.25.8` | 2024-03-27 | | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| @ -188,9 +192,26 @@ npx gatsby telemetry --disable | ||||
| 1) Create a template site: | ||||
| 
 | ||||
| ```bash | ||||
| npm init gatsby -- -y sheetjs-gatsby | ||||
| npx gatsby new sheetjs-gatsby | ||||
| ``` | ||||
| 
 | ||||
| :::info pass | ||||
| 
 | ||||
| For older Gatsby versions, the project must be built from the starter project. | ||||
| 
 | ||||
| For GatsbyJS 4, the starter commit is `6bc4466090845f20650117b3d27e68e6e46dc8d5` | ||||
| and the steps are shown below: | ||||
| 
 | ||||
| ```bash | ||||
| git clone https://github.com/gatsbyjs/gatsby-starter-default sheetjs-gatsby | ||||
| cd sheetjs-gatsby | ||||
| git checkout 6bc4466090845f20650117b3d27e68e6e46dc8d5 | ||||
| npm install | ||||
| cd .. | ||||
| ``` | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| 2) Follow the on-screen instructions for starting the local development server: | ||||
| 
 | ||||
| ```bash | ||||
| @ -222,6 +243,18 @@ npm i --save gatsby-transformer-excel gatsby-source-filesystem | ||||
| `} | ||||
| </CodeBlock> | ||||
| 
 | ||||
| :::info pass | ||||
| 
 | ||||
| For older versions of Gatsby, older versions of the dependencies must be used. | ||||
| 
 | ||||
| For GatsbyJS 4, the plugin version numbers align with the Gatsby version: | ||||
| 
 | ||||
| ```bash | ||||
| npm i --save gatsby-transformer-excel@4 gatsby-source-filesystem@4 | ||||
| ``` | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| 5) Make a `src/data` directory, download <https://sheetjs.com/pres.xlsx>, and | ||||
| move the downloaded file into the new folder: | ||||
| 
 | ||||
| @ -253,6 +286,27 @@ module.exports = { | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| :::note pass | ||||
| 
 | ||||
| If the `plugins` array exists, the two plugins should be added at the beginning: | ||||
| 
 | ||||
| ```js title="gatsby-config.js (add highlighted lines)" | ||||
|   plugins: [ | ||||
| // highlight-start | ||||
|     { | ||||
|       resolve: `gatsby-source-filesystem`, | ||||
|       options: { | ||||
|         name: `data`, | ||||
|         path: `${__dirname}/src/data/`, | ||||
|       }, | ||||
|     }, | ||||
|     `gatsby-transformer-excel`, | ||||
| // highlight-end | ||||
|     // ... | ||||
| ``` | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| Stop and restart the development server process (`npm run develop`). | ||||
| 
 | ||||
| ### GraphiQL test | ||||
| @ -322,7 +376,7 @@ displayed JSON is the data that the component receives: | ||||
| 
 | ||||
| 9) Change `PageComponent` to display a table based on the data: | ||||
| 
 | ||||
| ```jsx title="src/pages/pres.js" | ||||
| ```jsx title="src/pages/pres.js (replace PageComponent)" | ||||
| import { graphql } from "gatsby" | ||||
| import * as React from "react" | ||||
| 
 | ||||
|  | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -159,6 +159,7 @@ This demo was tested in the following deployments: | ||||
| | Architecture | NodeJS    | Date       | | ||||
| |:-------------|:----------|:-----------| | ||||
| | `darwin-x64` | `20.11.1` | 2024-03-17 | | ||||
| | `win10-x64`  | `20.12.0` | 2024-03-26 | | ||||
| | `linux-x64`  | `20.11.1` | 2024-03-18 | | ||||
| 
 | ||||
| ::: | ||||
| @ -224,7 +225,7 @@ local NodeJS platform. | ||||
| 4) Download the test file <https://sheetjs.com/pres.numbers>: | ||||
| 
 | ||||
| ```bash | ||||
| curl -LO https://sheetjs.com/pres.numbers | ||||
| curl -o pres.numbers https://sheetjs.com/pres.numbers | ||||
| ``` | ||||
| 
 | ||||
| 5) Run the script and pass `pres.numbers` as the first argument: | ||||
| @ -257,24 +258,54 @@ node --experimental-sea-config sheet2csv.json | ||||
| 
 | ||||
| ### SEA Injection | ||||
| 
 | ||||
| 8) Create a local copy of the NodeJS binary. On macOS and Linux: | ||||
| 8) Create a local copy of the NodeJS binary: | ||||
| 
 | ||||
| <Tabs groupId="triple"> | ||||
|   <TabItem value="darwin-x64" label="MacOS"> | ||||
| 
 | ||||
| ```bash | ||||
| cp `which node` sheet2csv | ||||
| ``` | ||||
| 
 | ||||
| <Tabs groupId="triple"> | ||||
|   <TabItem value="darwin-x64" label="MacOS"> | ||||
| 
 | ||||
| 9) Remove the code signature. | ||||
| 
 | ||||
| ```bash | ||||
| codesign --remove-signature ./sheet2csv | ||||
| ``` | ||||
| 
 | ||||
|   </TabItem> | ||||
|   <TabItem value="win10-x64" label="Windows"> | ||||
| 
 | ||||
| In PowerShell, the `Get-Command` command displays the location to `node.exe`: | ||||
| 
 | ||||
| ```powershell | ||||
| PS C:\sheetjs-sea> get-command node | ||||
| 
 | ||||
| CommandType     Name                                               Version    Source | ||||
| -----------     ----                                               -------    ------ | ||||
| Application     node.exe                                           20.12.0.0  C:\Program Files\nodejs\node.exe | ||||
| 
 | ||||
| ``` | ||||
| 
 | ||||
| Copy the program (listed in the "Source" column) to `sheet2csv.exe`: | ||||
| 
 | ||||
| ```powershell | ||||
| PS C:\sheetjs-sea> copy "C:\Program Files\nodejs\node.exe" sheet2csv.exe | ||||
| ``` | ||||
| 
 | ||||
| 9) Remove the code signature. | ||||
| 
 | ||||
| ```powershell | ||||
| signtool remove /s .\sheet2csv.exe | ||||
| ``` | ||||
| 
 | ||||
|   </TabItem> | ||||
|   <TabItem value="linux-x64" label="Linux"> | ||||
| 
 | ||||
| ```bash | ||||
| cp `which node` sheet2csv | ||||
| ``` | ||||
| 
 | ||||
| 9) Observe that many Linux distributions do not enforce code signatures. | ||||
| 
 | ||||
|   </TabItem> | ||||
| @ -293,6 +324,29 @@ npx -y postject --sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2 - | ||||
| 
 | ||||
| ```bash | ||||
| codesign -s - ./sheet2csv | ||||
| ``` | ||||
| 
 | ||||
|   </TabItem> | ||||
|   <TabItem value="win10-x64" label="Windows"> | ||||
| 
 | ||||
| ```bash | ||||
| npx -y postject --sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2 sheet2csv.exe NODE_SEA_BLOB sheet2csv.blob | ||||
| ``` | ||||
| 
 | ||||
| 11) Resign the binary. | ||||
| 
 | ||||
| The following sequence generates a self-signed certificate: | ||||
| 
 | ||||
| ```powershell | ||||
| $cert = New-SelfSignedCertificate -Type CodeSigning -DnsName www.onlyspans.net -CertStoreLocation Cert:\CurrentUser\My | ||||
| $pass = ConvertTo-SecureString -String "hunter2" -Force -AsPlainText | ||||
| Export-PfxCertificate -Cert "cert:\CurrentUser\My\$($cert.Thumbprint)" -FilePath "mycert.pfx" -Password $pass | ||||
| ``` | ||||
| 
 | ||||
| After creating a cert, sign the binary: | ||||
| 
 | ||||
| ```powershell | ||||
| signtool sign /v /f mycert.pfx /p hunter2 /fd SHA256 sheet2csv.exe | ||||
| ``` | ||||
| 
 | ||||
|   </TabItem> | ||||
| @ -321,7 +375,7 @@ The program should display the same CSV contents as the script (from step 5) | ||||
| <Tabs groupId="triple"> | ||||
|   <TabItem value="darwin-x64" label="MacOS"> | ||||
| 
 | ||||
| 13) Validate the binary signature. On macOS: | ||||
| 13) Validate the binary signature: | ||||
| 
 | ||||
| ```bash | ||||
| codesign -dv ./sheet2csv | ||||
| @ -333,6 +387,24 @@ Inspecting the output, the following line confirms ad-hoc signing was used: | ||||
| Signature=adhoc | ||||
| ``` | ||||
| 
 | ||||
|   </TabItem> | ||||
|   <TabItem value="win10-x64" label="Windows"> | ||||
| 
 | ||||
| 13) Validate the binary signature: | ||||
| 
 | ||||
| ```powershell | ||||
| signtool verify sheet2csv.exe | ||||
| ``` | ||||
| 
 | ||||
| If the certificate is self-signed, there may be an error: | ||||
| 
 | ||||
| ``` | ||||
| SignTool Error: A certificate chain processed, but terminated in a root | ||||
|         certificate which is not trusted by the trust provider. | ||||
| ``` | ||||
| 
 | ||||
| This error is expected. | ||||
| 
 | ||||
|   </TabItem> | ||||
|   <TabItem value="linux-x64" label="Linux"> | ||||
| 
 | ||||
|  | ||||
| @ -36,8 +36,9 @@ This demo was tested in the following environments: | ||||
| 
 | ||||
| | Postgres | Connector Library | Date       | | ||||
| |:---------|:------------------|:-----------| | ||||
| | `16.0.1` | `pg` (`8.11.3`)   | 2023-10-30 | | ||||
| | `15.5`   | `pg` (`8.11.3`)   | 2023-12-04 | | ||||
| | `16.2.1` | `pg` (`8.11.4`)   | 2024-03-31 | | ||||
| | `15.6`   | `pg` (`8.11.4`)   | 2024-03-31 | | ||||
| | `14.11`  | `pg` (`8.11.4`)   | 2024-03-31 | | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| @ -275,7 +276,7 @@ npm init -y | ||||
| 4) Install the `pg` connector module: | ||||
| 
 | ||||
| ```bash | ||||
| npm i --save pg@8.11.3 | ||||
| npm i --save pg@8.11.4 | ||||
| ``` | ||||
| 
 | ||||
| 5) Save the following example codeblock to `PGTest.js`: | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| --- | ||||
| title: Sheets with MongoDB | ||||
| sidebar_label: MongoDB | ||||
| sidebar_label: MongoDB / FerretDB | ||||
| pagination_prev: demos/cli/index | ||||
| pagination_next: demos/local/index | ||||
| sidebar_custom_props: | ||||
| @ -24,10 +24,11 @@ to add data from spreadsheets into a collection. | ||||
| 
 | ||||
| This demo was tested in the following environments: | ||||
| 
 | ||||
| | MongoDB CE | Connector Library   | Date       | | ||||
| |:-----------|:--------------------|:-----------| | ||||
| | `6.0.10`   | `mongodb` (`5.7.0`) | 2023-12-04 | | ||||
| | `7.0.2`    | `mongodb` (`5.7.0`) | 2023-12-04 | | ||||
| | Server              | Connector Library   | Date       | | ||||
| |:--------------------|:--------------------|:-----------| | ||||
| | FerretDB `1.21.0`   | `mongodb` (`5.9.2`) | 2024-03-30 | | ||||
| | MongoDB CE `6.0.10` | `mongodb` (`5.7.0`) | 2023-12-04 | | ||||
| | MongoDB CE `7.0.2`  | `mongodb` (`5.7.0`) | 2023-12-04 | | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| @ -81,7 +82,14 @@ This workbook is typically exported to the filesystem with `writeFile`[^8]. | ||||
| 
 | ||||
| ## Complete Example | ||||
| 
 | ||||
| 0) Install MongoDB 7.0 Community Edition[^9]. The macOS steps required `brew`: | ||||
| 0) Install a MongoDB-compatible server. Options include MongoDB CE[^9] and | ||||
| FerretDB[^10] | ||||
| 
 | ||||
| 1) Start a server on `localhost` (follow official instructions). | ||||
| 
 | ||||
| <details><summary><b>MongoDB CE Setup</b> (click to show)</summary> | ||||
| 
 | ||||
| For MongoDB 7.0 Community Edition, the macOS steps required `brew`: | ||||
| 
 | ||||
| ```bash | ||||
| brew tap mongodb/brew | ||||
| @ -89,8 +97,6 @@ brew update | ||||
| brew install mongodb-community | ||||
| ``` | ||||
| 
 | ||||
| 1) Start a MongoDB server on `localhost` (follow official instructions). | ||||
| 
 | ||||
| :::note pass | ||||
| 
 | ||||
| If `brew` was used to install MongoDB, the following command starts a server: | ||||
| @ -107,6 +113,8 @@ If Homebrew is configured to use `/opt/homebrew`, the command is: | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| </details> | ||||
| 
 | ||||
| 2) Create base project and install the dependencies: | ||||
| 
 | ||||
| <CodeBlock language="bash">{`\ | ||||
| @ -179,4 +187,5 @@ There should be no errors in the terminal. The script will generate the file | ||||
| [^6]: See [`json_to_sheet` in "Utilities"](/docs/api/utilities/array#array-of-objects-input) | ||||
| [^7]: See ["Workbook Helpers" in "Utilities"](/docs/api/utilities/wb) for details on `book_new` and `book_append_sheet`. | ||||
| [^8]: See [`writeFile` in "Writing Files"](/docs/api/write-options) | ||||
| [^9]: See ["Install MongoDB Community Edition"](https://www.mongodb.com/docs/manual/administration/install-community/#std-label-install-mdb-community-edition) in the MongoDB documentation. | ||||
| [^9]: See ["Install MongoDB Community Edition"](https://www.mongodb.com/docs/manual/administration/install-community/#std-label-install-mdb-community-edition) in the MongoDB documentation. | ||||
| [^10]: See ["SQLite Setup with Docker Compose"](https://docs.ferretdb.io/quickstart-guide/docker/#sqlite-setup-with-docker-compose) in the FerretDB documentation. | ||||
| @ -17,8 +17,10 @@ import CodeBlock from '@theme/CodeBlock'; | ||||
| 
 | ||||
| **Redis has relicensed away from open source!** | ||||
| 
 | ||||
| The original BSD-3-Clause applies to version `7.2.4`. This discussion applies to | ||||
| KeyDB and other servers that support the "Redis serialization protocol" (RESP). | ||||
| The original BSD-3-Clause still applies to version `7.2.4`. | ||||
| 
 | ||||
| This demo has been tested with KeyDB and other servers that support the "Redis | ||||
| serialization protocol" (RESP). | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| @ -40,7 +42,7 @@ This demo was tested in the following environments: | ||||
| |:--------------|:-------------------|:----------:| | ||||
| | KeyDB `6.3.4` | `redis` (`4.6.13`) | 2024-03-25 | | ||||
| | Redis `6.2.9` | `redis` (`4.6.11`) | 2023-12-04 | | ||||
| | Redis `7.2.3` | `redis` (`4.6.11`) | 2023-12-04 | | ||||
| | Redis `7.2.4` | `redis` (`4.6.11`) | 2024-03-26 | | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
|  | ||||
| @ -4,6 +4,11 @@ pagination_prev: demos/cloud/index | ||||
| pagination_next: demos/bigdata/index | ||||
| --- | ||||
| 
 | ||||
| import current from '/version.js'; | ||||
| import Tabs from '@theme/Tabs'; | ||||
| import TabItem from '@theme/TabItem'; | ||||
| import CodeBlock from '@theme/CodeBlock'; | ||||
| 
 | ||||
| The [SheetJS Standalone scripts](/docs/getting-started/installation/standalone) | ||||
| can be integrated in a Chromium extension. | ||||
| 
 | ||||
| @ -14,7 +19,7 @@ tables with a content script and a background script. | ||||
| 
 | ||||
| :::note Tested Deployments | ||||
| 
 | ||||
| This demo was last tested on 2024 March 11 against Chrome 122. | ||||
| This demo was last tested on 2024 March 30 against Chrome 122. | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| @ -131,7 +136,8 @@ for(var i = 0; i < tables.length; ++i) { | ||||
| The demo extension includes multiple features to demonstrate sample usage. | ||||
| Production extensions should include proper error handling. | ||||
| 
 | ||||
| <details open><summary><b>Testing Unpacked Extension</b> (click to hide)</summary> | ||||
| <Tabs> | ||||
|   <TabItem name="zip" value="Prepared Extension"> | ||||
| 
 | ||||
| 1) Download the zip for the desired Manifest version: | ||||
| 
 | ||||
| @ -142,13 +148,186 @@ Production extensions should include proper error handling. | ||||
| 
 | ||||
| 3) Drag and drop the downloaded zip file into the window. | ||||
| 
 | ||||
| </details> | ||||
|   </TabItem> | ||||
|   <TabItem name="crx" value="Extension from Scratch"> | ||||
| 
 | ||||
| 1) Create a new extension using `create-chrome-ext`[^1]: | ||||
| 
 | ||||
| ```bash | ||||
| npm create chrome-ext@latest sheetjs-crx -- --template vanilla-ts | ||||
| cd sheetjs-crx | ||||
| npm install | ||||
| ``` | ||||
| 
 | ||||
| 2) Edit the highlighted lines in `package.json`: | ||||
| 
 | ||||
| ```js title="package.json" (edit highlighted lines) | ||||
| { | ||||
|   "name": "sheetjs-crx", | ||||
|   // highlight-next-line | ||||
|   "displayName": "SheetJS Demo", | ||||
|   "version": "0.0.0", | ||||
|   "author": "**", | ||||
|   // highlight-next-line | ||||
|   "description": "Sample Extension using SheetJS to interact with Chrome",   | ||||
| ``` | ||||
| 
 | ||||
| 3) Edit `manifest.ts` and add to the `permissions` array: | ||||
| 
 | ||||
| ```ts title="manifest.ts" | ||||
|   permissions: ['sidePanel', 'storage', | ||||
|     "activeTab", | ||||
|     "bookmarks", | ||||
|     "contextMenus", | ||||
|     "downloads", | ||||
|     "tabs" | ||||
|   ], | ||||
| ``` | ||||
| 
 | ||||
| 4) Install the SheetJS dependency and start the dev server: | ||||
| 
 | ||||
| <CodeBlock language="bash">{`\ | ||||
| curl -o .\public\img\logo-48.png https://docs.sheetjs.com/logo.png | ||||
| npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz | ||||
| npm run dev`} | ||||
| </CodeBlock> | ||||
| 
 | ||||
| The build step will create a `build` subfolder. | ||||
| 
 | ||||
| 5) Replace `src/popup/index.ts` with the following codeblock: | ||||
| 
 | ||||
| ```ts title="src/popup/index.ts" | ||||
| import { version, utils, writeFileXLSX } from 'xlsx'; | ||||
| import './index.css' | ||||
| 
 | ||||
| /* recursively walk the bookmark tree */ | ||||
| const recurse_bookmarks = (data, tree, path) => { | ||||
|   if(tree.url) data.push({Name: tree.title, Location: tree.url, Path:path}); | ||||
|   var T = path ? (path + "::" + tree.title) : tree.title; | ||||
|   (tree.children||[]).forEach(function(C) { recurse_bookmarks(data, C, T); }); | ||||
| }; | ||||
| 
 | ||||
| const export_bookmarks = () => { | ||||
|   chrome.bookmarks.getTree(function(res) { | ||||
|     var data = []; | ||||
|     res.forEach(function(t) { recurse_bookmarks(data, t, ""); }); | ||||
| 
 | ||||
|     /* create worksheet */ | ||||
|     var ws = utils.json_to_sheet(data, { header: ['Name', 'Location', 'Path'] }); | ||||
| 
 | ||||
|     /* create workbook and export */ | ||||
|     var wb = utils.book_new(); | ||||
|     utils.book_append_sheet(wb, ws, 'Bookmarks'); | ||||
|     writeFileXLSX(wb, "bookmarks.xlsx"); | ||||
|   }); | ||||
| }; | ||||
| 
 | ||||
| document.addEventListener('DOMContentLoaded', () => { | ||||
|   const root = document.getElementById('app')! | ||||
| 
 | ||||
|   const xprt = document.createElement("button"); // sjsdownload | ||||
|   xprt.type = "button"; xprt.innerHTML = "Export Bookmarks"; | ||||
|   root.appendChild(xprt); | ||||
|   xprt.addEventListener("click", export_bookmarks); | ||||
| 
 | ||||
|   const vers = document.createElement("a"); | ||||
|   vers.innerHTML = "SheetJS " + version; | ||||
|   root.appendChild(vers); | ||||
|   vers.addEventListener("click", () => { chrome.tabs.create({url: "https://sheetjs.com/"}); }); | ||||
| }); | ||||
| ``` | ||||
| 
 | ||||
| 6) Replace `src/background/index.ts` with the following codeblock: | ||||
| 
 | ||||
| ```ts title="src/background/index.ts" | ||||
| chrome.runtime.onInstalled.addListener(function() { | ||||
| 	chrome.contextMenus.create({ | ||||
| 		type: "normal", | ||||
| 		id: "sjsexport", | ||||
| 		title: "Export Table to XLSX", | ||||
| 		contexts: ["page", "selection"] | ||||
| 	}); | ||||
| 	chrome.contextMenus.create({ | ||||
| 		type: "normal", | ||||
| 		id: "sj5export", | ||||
| 		title: "Export All Tables in Page", | ||||
| 		contexts: ["page", "selection"] | ||||
| 	}); | ||||
| 	chrome.contextMenus.onClicked.addListener(function(info/*, tab*/) { | ||||
| 		var mode = ""; | ||||
| 		switch(info.menuItemId) { | ||||
| 			case 'sjsexport': mode = "JS"; break; | ||||
| 			case 'sj5export': mode = "J5"; break; | ||||
| 			default: return; | ||||
| 		} | ||||
| 		chrome.tabs.query({active: true, currentWindow: true}, function(tabs){ | ||||
| 			chrome.tabs.sendMessage(tabs[0].id, {Sheet:mode}, sjsexport_cb); | ||||
| 		}); | ||||
| 	}); | ||||
| 
 | ||||
| 	chrome.contextMenus.create({ | ||||
| 		id: "sjsabout", | ||||
| 		title: "About", | ||||
| 		contexts: ["browser_action"] | ||||
| 	}); | ||||
| 	chrome.contextMenus.onClicked.addListener(function(info/*, tab*/) { | ||||
| 		if(info.menuItemId !== "sjsabout") return; | ||||
| 		chrome.tabs.create({url: "https://sheetjs.com/"}); | ||||
| 	}); | ||||
| }); | ||||
| 
 | ||||
| function sjsexport_cb(wb) { | ||||
| 	if(!wb || !wb.SheetNames || !wb.Sheets) { return alert("Error in exporting table"); } | ||||
| 	const b64 = XLSX.write(wb, {bookType: "xlsx", type: "base64"}); | ||||
| 	chrome.downloads.download({ | ||||
| 		url: `data:application/octet-stream;base64,${b64}`, | ||||
| 		filename: `SheetJSTables.xlsx` | ||||
| 	}) | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| 7) Replace `src/contentScript/index.ts` with the following codeblock: | ||||
| 
 | ||||
| ```ts title="src/contentScript/index.ts" | ||||
| import { utils } from 'xlsx'; | ||||
| var coords = [0,0]; | ||||
| document.addEventListener('mousedown', function(mouse) { | ||||
| 	if(mouse && mouse.button == 2) coords = [mouse.clientX, mouse.clientY]; | ||||
| }); | ||||
| 
 | ||||
| chrome.runtime.onMessage.addListener(function(msg, sender, cb) { | ||||
| 	if(!msg || !msg['Sheet']) return; | ||||
| 	if(msg.Sheet == "JS") { | ||||
| 		var elt = document.elementFromPoint(coords[0], coords[1]); | ||||
| 		while(elt != null) { | ||||
| 			if(elt.tagName.toLowerCase() == "table") return cb(utils.table_to_book(elt)); | ||||
| 			elt = elt.parentElement; | ||||
| 		} | ||||
| 	} else if(msg.Sheet == "J5") { | ||||
| 		var tables = document.getElementsByTagName("table"); | ||||
| 		var wb = utils.book_new(); | ||||
| 		for(var i = 0; i < tables.length; ++i) { | ||||
| 			var ws = utils.table_to_sheet(tables[i]); | ||||
| 			utils.book_append_sheet(wb, ws, "Table" + i); | ||||
| 		} | ||||
| 		return cb(wb); | ||||
| 	} | ||||
| 	cb(coords); | ||||
| }); | ||||
| ``` | ||||
| 
 | ||||
| 8) Open `chrome://extensions/` in the browser and enable Developer mode | ||||
| 
 | ||||
| 9) Click "Load unpacked" and select the `build` folder within the project. | ||||
| 
 | ||||
|   </TabItem> | ||||
| </Tabs> | ||||
| 
 | ||||
| ### Bookmark Exporter | ||||
| 
 | ||||
| <details open><summary><b>Testing</b> (click to hide)</summary> | ||||
| 
 | ||||
| 0) Go to <https://sheetjs.com> and create a bookmark in the browser. | ||||
| 0) Open <https://sheetjs.com> in the browser and create a bookmark. | ||||
| 
 | ||||
| 1) Click the Extensions icon (puzzle icon to the right of the address bar) and | ||||
| select "SheetJS Demo". | ||||
| @ -203,7 +382,7 @@ chrome.bookmarks.getTree(function(res) { | ||||
| 
 | ||||
| <details open><summary><b>Testing</b> (click to hide)</summary> | ||||
| 
 | ||||
| 1) Go to <https://sheetjs.com/demo/table> | ||||
| 1) Open <https://sheetjs.com/demo/table> in the browser. | ||||
| 
 | ||||
| 2) Right-click anywhere in the page and select "SheetJS Demo" > "Export All Tables in Page" | ||||
| 
 | ||||
| @ -255,3 +434,5 @@ sequenceDiagram | ||||
|   Note over P: Create Data URL | ||||
|   P->>U: `chrome.downloads.download` | ||||
| ``` | ||||
| 
 | ||||
| [^1]: See the [`create-chrome-ext` package](https://github.com/guocaoyi/create-chrome-ext) for more details. | ||||
| @ -30,7 +30,7 @@ flowchart LR | ||||
| 
 | ||||
| :::note Tested Deployments | ||||
| 
 | ||||
| This demo was last tested by SheetJS users on 2023 October 3 in Maple 2023. | ||||
| This demo was last tested by SheetJS users on 2024 March 31 in Maple 2024. | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| @ -54,8 +54,8 @@ The extension function ultimately pairs the SheetJS `read`[^2] and `write`[^3] | ||||
| methods to read data from the old file and write a new file: | ||||
| 
 | ||||
| ```js | ||||
| var wb = XLSX.read(original_file_data, {type: "buffer"}); | ||||
| var new_file_data = XLSX.write(wb, {type: "array", bookType: "xlsx"}); | ||||
| var workbook = XLSX.read(original_file_data, { type: "buffer" }); | ||||
| var new_file_data = XLSX.write(workbook, { type: "array", bookType: "xlsx" }); | ||||
| ``` | ||||
| 
 | ||||
| The extension function will receive a file name and perform the following steps: | ||||
| @ -81,8 +81,9 @@ flowchart LR | ||||
| 
 | ||||
| ### C Extensions | ||||
| 
 | ||||
| Maple C extensions are shared libraries or DLLs that use special Maple methods | ||||
| for parsing arguments and returning values. | ||||
| Maple extensions are shared libraries or DLLs that use special Maple methods for | ||||
| parsing arguments and returning values. They are typically written in the C | ||||
| programming language. | ||||
| 
 | ||||
| To simplify the flow, the new function will take one argument (the original file | ||||
| name) and return one value (the new file name). | ||||
| @ -125,7 +126,7 @@ with(ExcelTools); | ||||
| Import(SheetToXLSX("pres.numbers")) | ||||
| ``` | ||||
| 
 | ||||
| 0) Ensure "Windows Subsystem for Linux" (WSL) and Visual Studio are installed. | ||||
| 0) Install "Windows Subsystem for Linux" (WSL)[^5] and Visual Studio[^6]. | ||||
| 
 | ||||
| 1) Open a new "x64 Native Tools Command Prompt" window and create a project | ||||
| folder `c:\sheetjs-maple`: | ||||
| @ -137,11 +138,11 @@ cd sheetjs-maple | ||||
| ``` | ||||
| 
 | ||||
| 2) Copy the headers and `lib` files from the Maple folder to the project folder. | ||||
| For example, using Maple 2023 on Windows x64: | ||||
| For example, using Maple 2024 on Windows x64: | ||||
| 
 | ||||
| ```powershell | ||||
| copy "C:\Program Files\Maple 2023\extern\include\"*.h . | ||||
| copy "c:\Program Files\Maple 2023\bin.x86_64_WINDOWS"\*.lib . | ||||
| copy "C:\Program Files\Maple 2024\extern\include\"*.h . | ||||
| copy "c:\Program Files\Maple 2024\bin.x86_64_WINDOWS"\*.lib . | ||||
| ``` | ||||
| 
 | ||||
| 3) Run `bash` to enter WSL | ||||
| @ -206,4 +207,6 @@ The result will show the data from `pres.numbers` | ||||
| [^1]: See ["ExcelTools"](https://www.maplesoft.com/support/help/Maple/view.aspx?path=ExcelTools) in the Maple documentation. | ||||
| [^2]: See [`read` in "Reading Files"](/docs/api/parse-options) | ||||
| [^3]: See [`write` in "Writing Files"](/docs/api/write-options) | ||||
| [^4]: See ["C OpenMaple and ExternalCalling Application Program Interface (API)"](https://www.maplesoft.com/support/help/maple/view.aspx?path=OpenMaple%2FC%2FAPI) in the Maple documentation. | ||||
| [^4]: See ["C OpenMaple and ExternalCalling Application Program Interface (API)"](https://www.maplesoft.com/support/help/maple/view.aspx?path=OpenMaple%2FC%2FAPI) in the Maple documentation. | ||||
| [^5]: In a PowerShell terminal window, run `wsl --install Ubuntu` | ||||
| [^6]: See [the Visual Studio website](https://visualstudio.microsoft.com/#vs-section) for download links. In the Visual Studio Installer, install the "Desktop development with C++" workflow. | ||||
| @ -6,8 +6,7 @@ sidebar_position: 1 | ||||
| import Tabs from '@theme/Tabs'; | ||||
| import TabItem from '@theme/TabItem'; | ||||
| 
 | ||||
| <details> | ||||
|   <summary><b>File Format Support</b> (click to show)</summary> | ||||
| <details><summary><b>File Format Support</b> (click to show)</summary> | ||||
| 
 | ||||
| Dates are a core concept in nearly every spreadsheet application in existence. | ||||
| Some legacy spreadsheet apps only supported dates.  Others supported times as a | ||||
| @ -254,7 +253,7 @@ if(!(wb?.Workbook?.WBProps?.date1904)) { | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| :::note Why does the 1904 date system exist? | ||||
| <details><summary><b>Why does the 1904 date system exist?</b> (click to show) </summary> | ||||
| 
 | ||||
| 1900 was not a leap year.  For the Gregorian calendar, the general rules are: | ||||
| - every multiple of 400 is a leap year | ||||
| @ -268,13 +267,13 @@ the `@date` function: | ||||
| ``` | ||||
| @date(0,2,28) -> 59    // Lotus accepts 2/28/1900 | ||||
| @date(0,2,29) -> 60    // <--2/29/1900 was not a real date | ||||
| @date(0.2,30) -> ERR   // Lotus rejects 2/30/1900 | ||||
| @date(0,2,30) -> ERR   // Lotus rejects 2/30/1900 | ||||
| ``` | ||||
| 
 | ||||
| Excel extends the tradition in the default date system.  The 1904 date system | ||||
| starts the count in 1904, skipping the bad date. | ||||
| 
 | ||||
| ::: | ||||
| </details> | ||||
| 
 | ||||
| ### Relative Epochs | ||||
| 
 | ||||
| @ -292,6 +291,8 @@ of universal time. | ||||
| 
 | ||||
| ## How Files Store Dates and Times | ||||
| 
 | ||||
| <details><summary><b>Technical Details</b> (click to show)</summary> | ||||
| 
 | ||||
| XLS, XLSB, and most binary formats store the raw date codes.  Special number | ||||
| formats are used to indicate that the values are intended to be dates/times. | ||||
| 
 | ||||
| @ -310,6 +311,8 @@ Numbers uses a calendar date system, but records pure time values as if they are | ||||
| absolute times in 1904 January 01. It is spiritually equivalent to the 1904 mode | ||||
| in Excel and other spreadsheet applications. | ||||
| 
 | ||||
| </details> | ||||
| 
 | ||||
| ## How JavaScript Engines Understand Time | ||||
| 
 | ||||
| JavaScript provides a `Date` object which represents an *absolute* time. Under | ||||
| @ -632,4 +635,22 @@ A single `Array#map` operation can create a fixed dataset: | ||||
| 
 | ||||
| ```js | ||||
| const new_rows = rows.map(({birthday, ...rest}) => ({birthday: new Date(birthday), ...rest})) | ||||
| ``` | ||||
| ``` | ||||
| 
 | ||||
| The `Date` constructor interprets the dates in local time. | ||||
| 
 | ||||
| :::caution pass | ||||
| 
 | ||||
| Excel and other spreadsheet software do not typically support dates before 1900. | ||||
| If there are dates before the threshold, it is strongly recommended to pass | ||||
| strings instead of `Date` objects. | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| :::warning pass | ||||
| 
 | ||||
| JavaScript string to `Date` conversion is "implementation-dependent" and may | ||||
| misinterpret some date formats. When designing APIs, it is strongly recommended | ||||
| to pass ISO 8601 strings when possible. | ||||
| 
 | ||||
| ::: | ||||
|  | ||||
| @ -4,8 +4,7 @@ sidebar_label: Hyperlinks | ||||
| sidebar_position: 3 | ||||
| --- | ||||
| 
 | ||||
| <details> | ||||
|   <summary><b>File Format Support</b> (click to show)</summary> | ||||
| <details><summary><b>File Format Support</b> (click to show)</summary> | ||||
| 
 | ||||
| Traditional spreadsheet software, including Excel, support "Cell Links". The | ||||
| entire cell text is clickable. | ||||
| @ -31,17 +30,34 @@ and writers apply the hyperlink to the entire cell text. | ||||
| 
 | ||||
| </details> | ||||
| 
 | ||||
| Hyperlinks are stored in the `l` key of cell objects.  The `Target` field of the | ||||
| hyperlink object is the target of the link, including the URI fragment. Tooltips | ||||
| are stored in the `Tooltip` field and are displayed when hovering over the text. | ||||
| Spreadsheet hyperlinks are clickable references to other locations. They serve | ||||
| the same role as the HTML `<a>` tag. | ||||
| 
 | ||||
| Spreadsheet applications can process "internal" (cells, ranges, and defined | ||||
| names) and "external" (websites, email addresses, and local files) references. | ||||
| 
 | ||||
| SheetJS hyperlink objects are stored in the `l` key of cell objects. Hyperlink | ||||
| objects include the following fields: | ||||
| 
 | ||||
| - `Target` (required) describes the reference. | ||||
| - `Tooltip` is the tooltip text. Tooltips are shown when hovering over the text. | ||||
| 
 | ||||
| For example, the following snippet creates a link from cell `A1` to | ||||
| <https://sheetjs.com> with the tip `"Find us @ SheetJS.com!"`: | ||||
| 
 | ||||
| ```js | ||||
| ws["A1"].l = { Target: "https://sheetjs.com", Tooltip: "Find us @ SheetJS.com!" }; | ||||
| /* create worksheet with cell A1 = "https://sheetjs.com" */ | ||||
| var ws = XLSX.utils.aoa_to_sheet([["https://sheetjs.com"]]); | ||||
| 
 | ||||
| /* add hyperlink */ | ||||
| ws["A1"].l = { | ||||
|   Target: "https://sheetjs.com", | ||||
|   Tooltip: "Find us @ SheetJS.com!" | ||||
| }; | ||||
| ``` | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| :::note pass | ||||
| 
 | ||||
| Following traditional software, hyperlinks are applied to entire cell objects. | ||||
| @ -60,11 +76,26 @@ general hyperlink styling. | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| 
 | ||||
| ## External Hyperlinks | ||||
| 
 | ||||
| Spreadsheet software will typically launch other programs to handle external | ||||
| hyperlinks. For example, clicking a "Web Link" will open a new browser window. | ||||
| 
 | ||||
| ### Web Links | ||||
| 
 | ||||
| HTTP and HTTPS links can be used directly: | ||||
| 
 | ||||
| ```js | ||||
| ws["A2"].l = { Target: "https://docs.sheetjs.com/docs/csf/features/hyperlinks#web-links" }; | ||||
| ws["A3"].l = { Target: "http://localhost:7262/yes_localhost_works" }; | ||||
| ``` | ||||
| 
 | ||||
| <details open><summary><b>Live Example</b> (click to hide)</summary> | ||||
| 
 | ||||
| ```jsx live | ||||
| /* The live editor requires this function wrapper */ | ||||
| function ExportSimpleLink(props) { return ( <button onClick={() => { | ||||
| function ExportSimpleLink() { return ( <button onClick={() => { | ||||
|   /* Create worksheet */ | ||||
|   var ws = XLSX.utils.aoa_to_sheet([ [ "Link", "No Link" ] ]); | ||||
|   /* Add link */ | ||||
| @ -82,54 +113,7 @@ function ExportSimpleLink(props) { return ( <button onClick={() => { | ||||
| 
 | ||||
| </details> | ||||
| 
 | ||||
| 
 | ||||
| <details><summary><b>Extract all links from a file</b> (click to show)</summary> | ||||
| 
 | ||||
| The following example iterates through each worksheet and each cell to find all | ||||
| links. The table shows sheet name, cell address, and target for each link. | ||||
| 
 | ||||
| ```jsx live | ||||
| function SheetJSParseLinks(props) { | ||||
|   const [rows, setRows] = React.useState([]); | ||||
| 
 | ||||
|   return ( <> | ||||
|     <input type="file" onChange={async(e) => { | ||||
|       let rows = []; | ||||
|       /* parse workbook */ | ||||
|       const file = e.target.files[0]; | ||||
|       const data = await file.arrayBuffer(); | ||||
|       const wb = XLSX.read(data); | ||||
| 
 | ||||
|       const html = []; | ||||
|       wb.SheetNames.forEach(n => { | ||||
|         var ws = wb.Sheets[n]; if(!ws) return; | ||||
|         var ref = XLSX.utils.decode_range(ws["!ref"]); | ||||
|         for(var R = 0; R <= ref.e.r; ++R) for(var C = 0; C <= ref.e.c; ++C) { | ||||
|           var addr = XLSX.utils.encode_cell({r:R,c:C}); | ||||
|           if(!ws[addr] || !ws[addr].l) continue; | ||||
|           var link = ws[addr].l; | ||||
|           rows.push({ws:n, addr, Target: link.Target}); | ||||
|         } | ||||
|       }); | ||||
|       setRows(rows); | ||||
|     }}/> | ||||
|     <table><tr><th>Sheet</th><th>Address</th><th>Link Target</th></tr> | ||||
|     {rows.map(r => (<tr><td>{r.ws}</td><td>{r.addr}</td><td>{r.Target}</td></tr>))} | ||||
|     </table> | ||||
|   </> ); | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| </details> | ||||
| 
 | ||||
| ## Remote Links | ||||
| 
 | ||||
| HTTP and HTTPS links can be used directly: | ||||
| 
 | ||||
| ```js | ||||
| ws["A2"].l = { Target: "https://docs.sheetjs.com/docs/csf/features/hyperlinks" }; | ||||
| ws["A3"].l = { Target: "http://localhost:7262/yes_localhost_works" }; | ||||
| ``` | ||||
| ### Mail Links | ||||
| 
 | ||||
| Excel also supports `mailto` email links with subject line: | ||||
| 
 | ||||
| @ -145,7 +129,7 @@ address input in the form never leaves your machine.** | ||||
| 
 | ||||
| ```jsx live | ||||
| /* The live editor requires this function wrapper */ | ||||
| function ExportRemoteLink(props) { | ||||
| function ExportRemoteLink() { | ||||
|   const [email, setEmail] = React.useState("ignored@dev.null"); | ||||
|   const set_email = React.useCallback((evt) => setEmail(evt.target.value)); | ||||
| 
 | ||||
| @ -172,7 +156,7 @@ function ExportRemoteLink(props) { | ||||
| 
 | ||||
| </details> | ||||
| 
 | ||||
| ## Local Links | ||||
| ### Local Links | ||||
| 
 | ||||
| Links to absolute paths should use the `file://` URI scheme: | ||||
| 
 | ||||
| @ -195,7 +179,7 @@ Relative Paths have undefined behavior in the SpreadsheetML 2003 format.  Excel | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| ## Internal Links | ||||
| ## Internal Hyperlinks | ||||
| 
 | ||||
| Links where the target is a cell or range or defined name in the same workbook | ||||
| ("Internal Links") are marked with a leading hash character: | ||||
| @ -218,7 +202,7 @@ The defined name `SheetJSDN` points to the range `A1:B2` in the second sheet. | ||||
| 
 | ||||
| ```jsx live | ||||
| /* The live editor requires this function wrapper */ | ||||
| function ExportInternalLink(props) { return ( <button onClick={() => { | ||||
| function ExportInternalLink() { return ( <button onClick={() => { | ||||
|   /* Create empty workbook */ | ||||
|   var wb = XLSX.utils.book_new(); | ||||
| 
 | ||||
| @ -279,7 +263,7 @@ HTML table. The hyperlink in the second row will be parsed as a cell-level link. | ||||
| 
 | ||||
| ```jsx live | ||||
| /* The live editor requires this function wrapper */ | ||||
| function ExportHyperlink(props) { | ||||
| function ExportHyperlink() { | ||||
| 
 | ||||
|   /* Callback invoked when the button is clicked */ | ||||
|   const xport = React.useCallback(() => { | ||||
| @ -314,7 +298,7 @@ has a standard HTML link. | ||||
| 
 | ||||
| ```jsx live | ||||
| /* The live editor requires this function wrapper */ | ||||
| function ExportALinks(props) { | ||||
| function ExportALinks() { | ||||
|   const [ __html, setHTML ] = React.useState(""); | ||||
|   React.useEffect(() => { | ||||
|     /* Create worksheet */ | ||||
| @ -335,5 +319,46 @@ function ExportALinks(props) { | ||||
| 
 | ||||
| </details> | ||||
| 
 | ||||
| #### Miscellany | ||||
| 
 | ||||
| <details><summary><b>Extract all links from a file</b> (click to show)</summary> | ||||
| 
 | ||||
| The following example iterates through each worksheet and each cell to find all | ||||
| links. The table shows sheet name, cell address, and target for each link. | ||||
| 
 | ||||
| ```jsx live | ||||
| function SheetJSParseLinks() { | ||||
|   const [rows, setRows] = React.useState([]); | ||||
| 
 | ||||
|   return ( <> | ||||
|     <input type="file" onChange={async(e) => { | ||||
|       let rows = []; | ||||
|       /* parse workbook */ | ||||
|       const file = e.target.files[0]; | ||||
|       const data = await file.arrayBuffer(); | ||||
|       const wb = XLSX.read(data); | ||||
| 
 | ||||
|       const html = []; | ||||
|       wb.SheetNames.forEach(n => { | ||||
|         var ws = wb.Sheets[n]; if(!ws) return; | ||||
|         var ref = XLSX.utils.decode_range(ws["!ref"]); | ||||
|         for(var R = 0; R <= ref.e.r; ++R) for(var C = 0; C <= ref.e.c; ++C) { | ||||
|           var addr = XLSX.utils.encode_cell({r:R,c:C}); | ||||
|           if(!ws[addr] || !ws[addr].l) continue; | ||||
|           var link = ws[addr].l; | ||||
|           rows.push({ws:n, addr, Target: link.Target}); | ||||
|         } | ||||
|       }); | ||||
|       setRows(rows); | ||||
|     }}/> | ||||
|     <table><tr><th>Sheet</th><th>Address</th><th>Link Target</th></tr> | ||||
|     {rows.map(r => (<tr><td>{r.ws}</td><td>{r.addr}</td><td>{r.Target}</td></tr>))} | ||||
|     </table> | ||||
|   </> ); | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| </details> | ||||
| 
 | ||||
| [^1]: The primary SheetJS DOM parsing methods are [`table_to_book`, `table_to_sheet`, and `sheet_add_dom`](/docs/api/utilities/html#html-table-input) | ||||
| [^2]: HTML strings can be written using [`bookType: "html"` in the `write` or `writeFile` methods](/docs/api/write-options) or by using the [dedicated `sheet_to_html` utility function](/docs/api/utilities/html#html-table-output) | ||||
| @ -1,11 +1,10 @@ | ||||
| --- | ||||
| title: Cell Comments and Notes | ||||
| sidebar_label: Cell Comments | ||||
| sidebar_position: 4 | ||||
| --- | ||||
| 
 | ||||
| # Cell Comments | ||||
| 
 | ||||
| <details> | ||||
|   <summary><b>File Format Support</b> (click to show)</summary> | ||||
| <details><summary><b>File Format Support</b> (click to show)</summary> | ||||
| 
 | ||||
| Comments and Notes have evolved over the years. | ||||
| 
 | ||||
| @ -42,6 +41,33 @@ The letter R (R) marks features parsed but not written in the format. | ||||
| 
 | ||||
| </details> | ||||
| 
 | ||||
| Comments and notes are cell annotations. Cells with comments or notes are marked | ||||
| with a small triangle or `¬` in the upper-right corner. | ||||
| 
 | ||||
| Excel notes are standalone text boxes with adjustable background colors and | ||||
| support for rich text. Historically people "replied" to comments by adding text | ||||
| to the end of existing comments. | ||||
| 
 | ||||
| Excel comments are simple text boxes that allow users to enter plain text. Users | ||||
| can reply to comments. | ||||
| 
 | ||||
| The following screenshot shows a spreadsheet with comments and a note. | ||||
| 
 | ||||
| - The note is associated with cell A1 (the cell with the red triangle). It has | ||||
| a green gradient background fill. | ||||
| - The comments are associated with cell A2 (the cell with the blue `¬`). There | ||||
| are 2 comments from different authors. A "Reply" box appears below the thread. | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| :::info pass | ||||
| 
 | ||||
| Google Sheets "notes" do not currently support rich text or background colors. | ||||
| 
 | ||||
| Apple Numbers supports "comments" but does not support "notes". | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| ## Basic Structure | ||||
| 
 | ||||
| Cell comments are objects stored in the `c` array of cell objects. | ||||
|  | ||||
| @ -1,11 +1,9 @@ | ||||
| --- | ||||
| title: Defined Names | ||||
| sidebar_position: 5 | ||||
| --- | ||||
| 
 | ||||
| # Defined Names | ||||
| 
 | ||||
| <details> | ||||
|   <summary><b>File Format Support</b> (click to show)</summary> | ||||
| <details><summary><b>File Format Support</b> (click to show)</summary> | ||||
| 
 | ||||
| Defined names have evolved over the decades, with new features added over time: | ||||
| 
 | ||||
| @ -27,7 +25,62 @@ no way to specify a Unicode defined name in the SYLK format. | ||||
| 
 | ||||
| </details> | ||||
| 
 | ||||
| `wb.Workbook.Names` is an array of defined name objects which have the keys: | ||||
| Defined names (sometimes called "named ranges") are labeled references to cells, | ||||
| ranges, constants or formulae. Meaningful labels can make formula expressions | ||||
| more readable and more robust to worksheet changes. | ||||
| 
 | ||||
| <details><summary><b>Why are Defined Names useful?</b> (click to show)</summary> | ||||
| 
 | ||||
| For example, the `NPV` formula function calculates the net present value of a | ||||
| series of cashflows. In large workbooks, raw data will be stored in separate | ||||
| worksheets and the interest rate will be stored in a separate "Model Parameters" | ||||
| worksheet. Formulae may have references to multiple sheets: | ||||
| 
 | ||||
| ``` | ||||
| =NPV('Model Parameters'!B2,Data!B2:F2) | ||||
|      ^^^^^^^^^^^^^^^^^^^^^ --- interest rate | ||||
| ``` | ||||
| 
 | ||||
| A defined name `Interest` referencing `'Model Parameters'!B2` would greatly | ||||
| simplify the formula: | ||||
| 
 | ||||
| ``` | ||||
| =NPV(Interest,Data!B2:F2) | ||||
|      ^^^^^^^^ --- interest rate | ||||
| ``` | ||||
| 
 | ||||
| Judicious use of Defined Names generally lead to fewer formula errors. | ||||
| 
 | ||||
| </details> | ||||
| 
 | ||||
| ## Storage | ||||
| 
 | ||||
| The `Workbook` property of SheetJS workbook objects store workbook attributes. | ||||
| The `Names` property of `Workbook` is an array of SheetJS defined name objects. | ||||
| 
 | ||||
| :::caution pass | ||||
| 
 | ||||
| Parsers do not always create the `Names` array or `Workbook` structure. Code | ||||
| should test for the existence of the defined names array before use: | ||||
| 
 | ||||
| ```js | ||||
| var wb = XLSX.utils.book_new(); | ||||
| 
 | ||||
| /* ensure the workbook structure exists */ | ||||
| /* highlight-start */ | ||||
| if(!wb.Workbook) wb.Workbook = {}; | ||||
| if(!wb.Workbook.Names) wb.Workbook.Names = []; | ||||
| /* highlight-end */ | ||||
| 
 | ||||
| /* add a new defined name */ | ||||
| wb.Workbook.Names.push({ Name: "MyData", Ref: "Sheet1!$A$1:$A$2" }); | ||||
| ``` | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| ## Defined Name Object | ||||
| 
 | ||||
| SheetJS defined name objects support the following properties: | ||||
| 
 | ||||
| | Key       | Name in app | Description                                        | | ||||
| |:----------|:------------|:---------------------------------------------------| | ||||
| @ -36,19 +89,7 @@ no way to specify a Unicode defined name in the SYLK format. | ||||
| | `Ref`     | "Refers To" | A1-Style Reference (`"Sheet1!$A$1:$D$20"`)         | | ||||
| | `Comment` | "Comment"   | Comment (for supported file formats)               | | ||||
| 
 | ||||
| Parsers do not always create the `Names` structure. Parsing and writing code | ||||
| should test for the existence of the defined names array before use: | ||||
| 
 | ||||
| ```js | ||||
| /* ensure the workbook structure exists */ | ||||
| if(!wb.Workbook) wb.Workbook = {}; | ||||
| if(!wb.Workbook.Names) wb.Workbook.Names = []; | ||||
| 
 | ||||
| /* add a new defined name */ | ||||
| wb.Workbook.Names.push({ Name: "MyData", Ref: "Sheet1!$A$1:$A$2" }); | ||||
| ``` | ||||
| 
 | ||||
| ## Ranges | ||||
| ### Ranges | ||||
| 
 | ||||
| Defined name references in formulae are internally shifted to the cell address. | ||||
| For example, given the defined name | ||||
| @ -73,7 +114,7 @@ The recommended approach is to fix the rows and columns of the reference: | ||||
| { Name: "MyData", Ref: "Sheet1!$A$1:$A$2" } // absolute reference | ||||
| ``` | ||||
| 
 | ||||
| ## Scoped Defined Names | ||||
| ### Scope | ||||
| 
 | ||||
| Excel allows two sheet-scoped defined names to share the same name.  However, a | ||||
| sheet-scoped name cannot collide with a workbook-scope name.  Workbook writers | ||||
|  | ||||
| @ -1,11 +1,9 @@ | ||||
| --- | ||||
| title: Number Formats | ||||
| sidebar_position: 6 | ||||
| --- | ||||
| 
 | ||||
| # Number Formats | ||||
| 
 | ||||
| <details> | ||||
|   <summary><b>File Format Support</b> (click to show)</summary> | ||||
| <details><summary><b>File Format Support</b> (click to show)</summary> | ||||
| 
 | ||||
| Modern applications separate "content" from "presentation". A value like `$3.50` | ||||
| is typically stored as the underlying value (`3.50`) with a format (`$0.00`). | ||||
| @ -64,19 +62,19 @@ To simplify editing, the applications will store the underlying values and the | ||||
| number formats separately. For example, `$3.50` will be represented as the value | ||||
| `3.5` with a number format that mandates a `$` sigil and 2 decimal places. | ||||
| 
 | ||||
| Number format metadata can be attached to each cell object in the `z` property: | ||||
| The `z` property of SheetJS cell objects stores the number format metadata: | ||||
| 
 | ||||
| ```js | ||||
| /* set the format of cell B2 to "0.00%" */ | ||||
| worksheet["B2"].z = "0.00%"; | ||||
| ``` | ||||
| 
 | ||||
| When requested, the cell formatted text will be stored in the `w` property. | ||||
| When requested, the formatted text will be stored in the `w` property. | ||||
| 
 | ||||
| ## Live Demo | ||||
| 
 | ||||
| This example generates a worksheet with common number formats. | ||||
| The number formats are explicitly assigned: | ||||
| This example generates a worksheet with common number formats. The number | ||||
| formats are explicitly assigned: | ||||
| 
 | ||||
| ```js | ||||
| /* assign number formats */ | ||||
|  | ||||
| @ -1,4 +1,5 @@ | ||||
| --- | ||||
| title: VBA and Macros | ||||
| sidebar_position: 7 | ||||
| --- | ||||
| 
 | ||||
| @ -7,13 +8,10 @@ import Tabs from '@theme/Tabs'; | ||||
| import TabItem from '@theme/TabItem'; | ||||
| import CodeBlock from '@theme/CodeBlock'; | ||||
| 
 | ||||
| # VBA and Macros | ||||
| <details><summary><b>File Format Support</b> (click to show)</summary> | ||||
| 
 | ||||
| <details> | ||||
|   <summary><b>File Format Support</b> (click to show)</summary> | ||||
| 
 | ||||
| Note that XLSX does not support macros. The XLSM file format is nearly | ||||
| identical to XLSX and supports macros. | ||||
| XLSX does not support macros. The XLSM file format is nearly identical to XLSX | ||||
| and supports macros. | ||||
| 
 | ||||
| | Formats | Basic | Storage Representation             | | ||||
| |:--------|:-----:|:-----------------------------------| | ||||
| @ -27,13 +25,39 @@ no way to embed VBA in the XLSX format. | ||||
| 
 | ||||
| </details> | ||||
| 
 | ||||
| VBA Macros are stored in a special data blob that is exposed in the `vbaraw` | ||||
| property of the workbook object when the `bookVBA` option is `true`.  They are | ||||
| 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. | ||||
| Visual Basic for Applications (VBA) is a scripting platform embedded in Excel. | ||||
| Users can include user-defined functions and macro code within spreadsheets. | ||||
| 
 | ||||
| :::note pass | ||||
| The `vbaraw` property of the SheetJS workbook object is an encoded data blob | ||||
| which includes the VBA macros and other metadata. | ||||
| 
 | ||||
| The SheetJS `read` and `readFile` methods do not pull VBA metadata by default. | ||||
| If the `bookVBA` option is set to true, the `vbaraw` blob is created. | ||||
| 
 | ||||
| ```js | ||||
| var workbook = XLSX.read(data, { bookVBA: true }); | ||||
| var encoded_vba_blob = workbook.vbaraw; | ||||
| ``` | ||||
| 
 | ||||
| The SheetJS `write` and `writeFile` methods will save the `vbaraw` blob if it is | ||||
| present in the workbook object and if the output file format supports macros. | ||||
| 
 | ||||
| ```js | ||||
| workbook.vbaraw = encoded_vba_blob; | ||||
| XLSX.writeFile(workbook, "SheetJSNewMacro.xlsm"); | ||||
| ``` | ||||
| 
 | ||||
| :::info pass | ||||
| 
 | ||||
| Newer versions of Excel support a new JavaScript API for writing user-defined | ||||
| functions. Those addins are not stored in the spreadsheet files. | ||||
| 
 | ||||
| [The "Excel JavaScript API" demo](/docs/demos/extensions/excelapi) covers usage | ||||
| of SheetJS libraries within the API. | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| :::tip pass | ||||
| 
 | ||||
| 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 | ||||
| @ -89,7 +113,7 @@ function SheetJSVBAFormula() { return ( <button onClick={async () => { | ||||
|   workbook.vbaraw = blob; | ||||
| 
 | ||||
|   /* create an XLSM file and try to save to SheetJSVBANeu.xlsm */ | ||||
|   XLSX.writeFile(workbook, "SheetJSVBANeu.xlsm", { bookVBA: true }); | ||||
|   XLSX.writeFile(workbook, "SheetJSVBANeu.xlsm"); | ||||
| }}><b>Click to Generate file!</b></button> ); } | ||||
| ``` | ||||
| 
 | ||||
| @ -129,7 +153,7 @@ XLSX.utils.book_append_sheet(workbook, worksheet, "Sheet1"); | ||||
| workbook.vbaraw = blob; | ||||
| 
 | ||||
| /* create an XLSM file and try to save to SheetJSVBANeu.xlsm */ | ||||
| XLSX.writeFile(workbook, "SheetJSVBANeu.xlsm", { bookVBA: true }); | ||||
| XLSX.writeFile(workbook, "SheetJSVBANeu.xlsm"); | ||||
| })(); | ||||
| ``` | ||||
| 
 | ||||
| @ -214,7 +238,6 @@ To ensure the writers export the VBA blob: | ||||
| 
 | ||||
| - The output format must support VBA (`xlsm` or `xlsb` or `xls` or `biff8`) | ||||
| - The workbook object must have a valid `vbaraw` field | ||||
| - The `write` or `writeFile` call must include the option `bookVBA: true` | ||||
| 
 | ||||
| This example uses [`vbaProject.bin`](pathname:///vba/vbaProject.bin) from the | ||||
| [sample file](pathname:///vba/SheetJSVBAFormula.xlsm): | ||||
| @ -241,7 +264,7 @@ function SheetJSVBAPrepared() { return ( <button onClick={async () => { | ||||
|   workbook.vbaraw = blob; | ||||
| 
 | ||||
|   /* create an XLSM file and try to save to SheetJSVBAPreparedNeu.xlsm */ | ||||
|   XLSX.writeFile(workbook, "SheetJSVBAPreparedNeu.xlsm", { bookVBA: true }); | ||||
|   XLSX.writeFile(workbook, "SheetJSVBAPreparedNeu.xlsm"); | ||||
| }}><b>Click to Generate file!</b></button> ); } | ||||
| ``` | ||||
| 
 | ||||
|  | ||||
| @ -3,8 +3,7 @@ title: Row Properties | ||||
| sidebar_position: 8 | ||||
| --- | ||||
| 
 | ||||
| <details> | ||||
|   <summary><b>File Format Support</b> (click to show)</summary> | ||||
| <details><summary><b>File Format Support</b> (click to show)</summary> | ||||
| 
 | ||||
| By default, all rows in a workbook are "Visible" and have a standard height. | ||||
| 
 | ||||
|  | ||||
| @ -3,8 +3,7 @@ title: Column Properties | ||||
| sidebar_position: 9 | ||||
| --- | ||||
| 
 | ||||
| <details> | ||||
|   <summary><b>File Format Support</b> (click to show)</summary> | ||||
| <details><summary><b>File Format Support</b> (click to show)</summary> | ||||
| 
 | ||||
| By default, all columns in a workbook are "Visible" and have a standard width. | ||||
| 
 | ||||
|  | ||||
| @ -3,8 +3,7 @@ title: Sheet Visibility | ||||
| sidebar_position: 10 | ||||
| --- | ||||
| 
 | ||||
| <details> | ||||
|   <summary><b>File Format Support</b> (click to show)</summary> | ||||
| <details><summary><b>File Format Support</b> (click to show)</summary> | ||||
| 
 | ||||
| By default, all sheets in a workbook are "Visible". The standard "Hidden" state | ||||
| is controlled through the context menu in the sheet tab bar. The "Very Hidden" | ||||
| @ -20,13 +19,13 @@ state is controlled through the "Visibility" property in the VBA editor. | ||||
| 
 | ||||
| </details> | ||||
| 
 | ||||
| Excel enables hiding sheets in the lower tab bar.  The sheet data is stored in | ||||
| the file but the UI does not readily make it available. | ||||
| Excel can hide sheet tabs from the lower tab bar. The sheet data is stored in | ||||
| the file but the tabs are not displayed until they are unhidden. | ||||
| 
 | ||||
| Standard "hidden" sheets are revealed in the "Unhide" menu. | ||||
| Standard "hidden" sheets are listed in the "Unhide" menu. | ||||
| 
 | ||||
| Excel also has "very hidden" sheets which cannot be revealed in the menu.  They | ||||
| are only accessible in the VB Editor! | ||||
| Excel "very hidden" sheets cannot be revealed in the menu. They are only visible | ||||
| in the Visual Basic Editor! | ||||
| 
 | ||||
| ## Storage | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										
											BIN
										
									
								
								docz/static/comments/types.png
									
									
									
									
									
										Normal file
									
								
							
							
								
									
								
								
								
								
								
									
									
								
							
						
						
									
										
											BIN
										
									
								
								docz/static/comments/types.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 163 KiB | 
							
								
								
									
										
											BIN
										
									
								
								docz/static/hyperlink/tooltip.png
									
									
									
									
									
										Normal file
									
								
							
							
								
									
								
								
								
								
								
									
									
								
							
						
						
									
										
											BIN
										
									
								
								docz/static/hyperlink/tooltip.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 43 KiB | 
		Loading…
	
		Reference in New Issue
	
	Block a user