visibility

This commit is contained in:
SheetJS 2023-08-20 16:39:35 -04:00
parent f270ad6b3f
commit 481b147e97
21 changed files with 326 additions and 149 deletions

@ -336,7 +336,7 @@ case it is easier to post-process the raw data:
```js
let last_year = 0;
raw_data.forEach(r => (r[0] != null) ? (last_year = r[0]) : (r[0] = last_year));
raw_data.forEach(r => last_year = r[0] = (r[0] != null ? r[0] : last_year));
```
:::caution pass
@ -411,6 +411,32 @@ raw_data.forEach(r => {
});
```
Observing that `r[0]` must equal `last_value`, the inner statement can be
rewritten to compute the final value and assign to both variables:
```js
let last_value = null;
raw_data.forEach(r => {
last_value = r[0] = (r[0] != null ? r[0] : last_value);
});
```
:::caution pass
It is tempting to take advantage of implicit logical rules:
```js
let last_value = null;
raw_data.forEach(r => {
last_value = r[0] = (r[0] || last_value);
});
```
This is strongly discouraged since the value `0` is false. The explicit `null`
test distinguishes `null` and `undefined` from `0`
:::
</details>
After post-processing, the rows now have proper year fields:
@ -443,7 +469,7 @@ function SheetJSAoAFilled() {
const raw_data = XLSX.utils.sheet_to_json(worksheet, {header:1});
/* fill years */
var last_year = 0;
raw_data.forEach(r => (r[0] != null) ? (last_year = r[0]) : (r[0] = last_year));
raw_data.forEach(r => last_year = r[0] = (r[0] != null ? r[0] : last_year));
/* pull Excel rows 13:16 (SheetJS 12:15) */
const rows_13_16 = raw_data.slice(12,16);
@ -479,7 +505,7 @@ function SheetJSAoAFiltered() {
const raw_data = XLSX.utils.sheet_to_json(worksheet, {header:1});
/* fill years */
var last_year = 0;
raw_data.forEach(r => (r[0] != null) ? (last_year = r[0]) : (r[0] = last_year));
raw_data.forEach(r => last_year = r[0] = (r[0] != null ? r[0] : last_year));
/* select data rows */
const rows = raw_data.filter(r => r[0] >= 2007 && r[0] <= 2023);
/* display data */
@ -553,7 +579,7 @@ function SheetJSObjects() {
const raw_data = XLSX.utils.sheet_to_json(worksheet, {header:1});
/* fill years */
var last_year = 0;
raw_data.forEach(r => (r[0] != null) ? (last_year = r[0]) : (r[0] = last_year));
raw_data.forEach(r => last_year = r[0] = (r[0] != null ? r[0] : last_year));
/* select data rows */
const rows = raw_data.filter(r => r[0] >= 2007 && r[0] <= 2023);
/* generate row objects */
@ -660,7 +686,7 @@ function StudentAidTotal() {
/* fill years */
var last_year = 0;
raw_data.forEach(r => (r[0] != null) ? (last_year = r[0]) : (r[0] = last_year));
raw_data.forEach(r => last_year = r[0] = (r[0] != null ? r[0] : last_year));
/* select data rows */
const rows = raw_data.filter(r => r[0] >= 2007 && r[0] <= 2023);
@ -715,7 +741,7 @@ Save the following script to `SheetJSStandaloneDemo.html`:
\n\
/* fill years */
var last_year = 0;
raw_data.forEach(r => (r[0] != null) ? (last_year = r[0]) : (r[0] = last_year));
raw_data.forEach(r => last_year = r[0] = (r[0] != null ? r[0] : last_year));
\n\
/* select data rows */
const rows = raw_data.filter(r => r[0] >= 2007 && r[0] <= 2023);
@ -768,7 +794,7 @@ const XLSX = require("xlsx");
/* fill years */
var last_year = 0;
raw_data.forEach(r => (r[0] != null) ? (last_year = r[0]) : (r[0] = last_year));
raw_data.forEach(r => last_year = r[0] = (r[0] != null ? r[0] : last_year));
/* select data rows */
const rows = raw_data.filter(r => r[0] >= 2007 && r[0] <= 2023);
@ -783,7 +809,6 @@ const XLSX = require("xlsx");
console.log(`${o.FY}\t${o.FQ||""}\t${o.total}`);
});
})();
```
After saving the script, run the script:
@ -829,7 +854,7 @@ Save the following script to `SheetJSNW.html`:
\n\
/* fill years */
var last_year = 0;
raw_data.forEach(r => (r[0] != null) ? (last_year = r[0]) : (r[0] = last_year));
raw_data.forEach(r => last_year = r[0] = (r[0] != null ? r[0] : last_year));
\n\
/* select data rows */
const rows = raw_data.filter(r => r[0] >= 2007 && r[0] <= 2023);
@ -923,7 +948,7 @@ const App = () => {
/* fill years */
var last_year = 0;
raw_data.forEach(r => (r[0] != null) ? (last_year = r[0]) : (r[0] = last_year));
raw_data.forEach(r => last_year = r[0] = (r[0] != null ? r[0] : last_year));
/* select data rows */
const rows = raw_data.filter(r => r[0] >= 2007 && r[0] <= 2023);

@ -453,7 +453,6 @@ const columns = Array.from({ length: range.e.c + 1 }, (_, i) => ({
/* column labels: encode_col translates 0 -> "A", 1 -> "B", 2 -> "C", ... */
name: XLSX.utils.encode_col(i)
}));
```
![Column labels for headers](pathname:///react/cols.png)

@ -94,7 +94,7 @@ npx browserify xlsxworker.js > worker.js
4) Spin up a local web server:
```
```bash
npx http-server
```
@ -1191,7 +1191,7 @@ This demo was last tested on 2023 May 07 against ViteJS `4.3.5`
1) Create a new ViteJS project:
```
```bash
npm create vite@latest sheetjs-vite -- --template vue-ts
cd sheetjs-vite
npm i
@ -1258,7 +1258,7 @@ writeFileXLSX(workbook, "Presidents.xlsx");
5) Run `npx vite build` and verify the generated pages work by running a local
web server in the `dist` folder:
```
```bash
npx http-server dist/
```

@ -265,7 +265,7 @@ Open that script.
Searching for `Bill Clinton` reveals the following:
```
```js
{"Name":"Bill Clinton","Index":42}
```

@ -215,7 +215,7 @@ const wb = XLSX.read(ab);
:::note
This demo was tested on an Intel Mac on 2023 July 02 with RN `0.72.1`.
This demo was tested on an Intel Mac on 2023 August 20 with RN `0.72.4`.
The iOS simulator runs iOS 16.2 on an iPhone SE (3rd generation).
@ -226,7 +226,7 @@ The Android simulator runs Android 12.0 (S) API 31 on a Pixel 3.
1) Create project:
```bash
npx -y react-native@0.72.1 init SheetJSRNFetch --version="0.72.1"
npx -y react-native@0.72.4 init SheetJSRNFetch --version="0.72.4"
```
2) Install shared dependencies:
@ -253,9 +253,9 @@ curl -LO https://docs.sheetjs.com/reactnative/App.tsx
When the demo was last tested on macOS, `java -version` displayed the following:
```
openjdk version "11.0.19" 2023-04-18 LTS
OpenJDK Runtime Environment Zulu11.64+19-CA (build 11.0.19+7-LTS)
OpenJDK 64-Bit Server VM Zulu11.64+19-CA (build 11.0.19+7-LTS, mixed mode)
openjdk version "11.0.20" 2023-07-18 LTS
OpenJDK Runtime Environment Zulu11.66+15-CA (build 11.0.20+8-LTS)
OpenJDK 64-Bit Server VM Zulu11.66+15-CA (build 11.0.20+8-LTS, mixed mode)
```
:::
@ -296,7 +296,7 @@ tapping "Import data from a spreadsheet", verify that the app shows new data:
:::warning pass
iOS testing requires macOS. It does not work on Windows.
iOS testing requires macOS. It does not work on Windows or Linux.
:::

@ -253,7 +253,6 @@ This uses two functions that should be added to the component script:
// highlight-end
}
});
```
The app should now show two buttons at the bottom:

@ -13,8 +13,11 @@ sidebar_custom_props:
import current from '/version.js';
import CodeBlock from '@theme/CodeBlock';
AlaSQL is a pure JavaScript in-memory SQL database. It has built-in support for
SheetJS through the `XLSX` target operator.
[AlaSQL](https://alasql.org/) is a pure JavaScript in-memory SQL database. It
has built-in support for SheetJS through the `XLSX` target operator.
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
data from spreadsheets.
This demo covers basic concepts pertaining to data import and export. The
official documentation includes advanced examples and deployment tips as well as
@ -27,7 +30,7 @@ This demo was tested in the following environments:
| Environment | AlaSQL | Date |
|:--------------------|:-------|:----------:|
| NodeJS | 3.1.0 | 2023-07-24 |
| Standalone (Chrome) | 3.0.0 | 2023-04-09 |
| Standalone (Chrome) | 3.0.0 | 2023-08-20 |
:::

@ -47,8 +47,8 @@ const ws = utils.json_to_sheet(aoo);
:::note
This demo was last tested on 2023 February 23 with MongoDB CE 6.0.4, MongoDB
connector module 5.1.0 and NodeJS 18.14.2.
This demo was last tested on 2023 August 20 with MongoDB CE 7.0.0, MongoDB
connector module 5.7.0 and NodeJS 20.5.1.
:::
@ -57,11 +57,11 @@ connector module 5.1.0 and NodeJS 18.14.2.
```bash
brew tap mongodb/brew
brew update
brew install mongodb-community@6.0
brew install mongodb-community
```
1) Start a MongoDB server on `localhost` (follow official instructions). To run
in the foreground on Intel MacOS:
1) Start a MongoDB server on `localhost` (follow official instructions).
If `brew` was used to install MongoDB, the following command starts a server:
```bash
/usr/local/opt/mongodb-community/bin/mongod --config /usr/local/etc/mongod.conf
@ -73,7 +73,7 @@ in the foreground on Intel MacOS:
mkdir sheetjs-mongo
cd sheetjs-mongo
npm init -y
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz mongodb@5.1.0`}
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz mongodb@5.7.0`}
</CodeBlock>
3) Save the following to `SheetJSMongoCRUD.mjs` (the key step is highlighted):

@ -24,7 +24,7 @@ This demo was verified by NetSuite consultants in the following deployments:
| `@NScriptType` | `@NApiVersion` | Date |
|:----------------|:---------------|:-----------|
| ScheduledScript | 2.1 | 2023-03-09 |
| ScheduledScript | 2.1 | 2023-08-18 |
| Restlet | 2.1 | 2023-04-20 |
| Suitelet | 2.1 | 2023-07-21 |
| MapReduceScript | 2.1 | 2023-07-31 |

@ -350,7 +350,6 @@ fn eval_code_ab(scope: &mut v8::HandleScope, code: &str) -> Vec<u8> {
result.byte_length()
).to_vec(); }
}
```
:::note

@ -299,7 +299,7 @@ curl -LO https://sheetjs.com/pres.numbers`}
6) Run the test program:
```
```bash
./sheetjs.ch pres.numbers
```
@ -361,6 +361,6 @@ ready, it will read the bundled test data and print the contents as CSV.
5) Run the script using the ChakraCore standalone binary:
```
```bash
./ch xlsx.chakra.js
```

@ -4,11 +4,18 @@ pagination_next: solutions/input
hide_table_of_contents: true
---
# Demo Projects
# Demos
Demos include complete examples and short discussions. For features that can
run in the web browser, demos will include interactive examples.
:::tip pass
If a demo for a library or framework is not included here, please leave a note
in the [issue tracker](https://git.sheetjs.com/sheetjs/docs.sheetjs.com/issues)
:::
### JavaScript APIs
- [`XMLHttpRequest and fetch`](/docs/demos/net/network)
@ -66,6 +73,7 @@ run in the web browser, demos will include interactive examples.
- [`NextJS`](/docs/demos/static/nextjs)
- [`NuxtJS`](/docs/demos/static/nuxtjs)
- [`SvelteKit`](/docs/demos/static/svelte)
- [`Webpack`](/docs/demos/static/webpack)
### App Extensions
@ -129,10 +137,3 @@ run in the web browser, demos will include interactive examples.
- [`QuickJS (C)`](/docs/demos/engines/quickjs)
- [`ExecJS (Ruby)`](/docs/demos/engines/rb)
- [`JavaScript::Engine (Perl)`](/docs/demos/engines/perl)
:::note
If a demo for a library or framework is not included here, please leave a note.
:::

@ -522,7 +522,7 @@ const workbook = XLSX.read(data);`}</CodeBlock>
Deno must be run with the `--allow-net` flag to enable network requests:
```
```bash
deno run --allow-net test-fetch.ts
```

@ -88,3 +88,12 @@ discussed in more detail in ["Defined Names"](/docs/csf/features/names)
| `CodeName` | [VBA Workbook Name](/docs/csf/features/vba) |
| `date1904` | epoch: 0/false for 1900 system, 1/true for 1904 |
| `filterPrivacy` | Warn or strip personally identifying info on save |
### Sheet Metadata
`wb.Workbook.Sheets` is an array of sheet metadata objects which have the keys:
| Key | Description |
|:----------------|:----------------------------------------------------|
| `Hidden` | [Sheet Visibility](/docs/csf/features/visibility) |
| `CodeName` | [VBA Sheet Code Name](/docs/csf/features/vba) |

@ -1,5 +1,5 @@
---
sidebar_position: 3
sidebar_position: 1
---
# Dates and Times

@ -1,5 +1,5 @@
---
sidebar_position: 1
sidebar_position: 2
---
# Formulae
@ -16,17 +16,17 @@ while the writer will translate from A1-Style strings to the file format.
|:------------------|:-----:|:-----:|:-----:|:-------:|:-----------------------|
| XLSX / XLSM | ✔ | ✔ | ✔ | ✔ | A1-Style strings |
| XLSB | ✔ | | ✔ | ✔ | BIFF parsed tokens |
| XLS | ✔ | | ✔ | * | BIFF parsed tokens |
| XLML | ✔ | ✔ | ✔ | * | RC-style strings |
| SYLK | ✔ | ✔ | | * | A1/RC-style strings |
| CSV / TXT | ✔ | ✔ | * | * | A1-Style strings |
| ODS / FODS / UOS | ✔ | ✔ | | * | OpenFormula strings |
| WK\* | ✔ | | | * | Lotus parsed tokens |
| WQ\* / WB\* / QPW | | | | * | Quattro Pro tokens |
| NUMBERS | | | | * | Numbers parsed tokens |
| XLS | ✔ | | ✔ | | BIFF parsed tokens |
| XLML | ✔ | ✔ | ✔ | | RC-style strings |
| SYLK | ✔ | ✔ | | | A1/RC-style strings |
| CSV / TXT | ✔ | ✔ | ✕ | ✕ | A1-Style strings |
| ODS / FODS / UOS | ✔ | ✔ | | | OpenFormula strings |
| WK\* | ✔ | | | | Lotus parsed tokens |
| WQ\* / WB\* / QPW | | | | | Quattro Pro tokens |
| NUMBERS | | | | | Numbers parsed tokens |
Asterisks (*) mark features that are not supported by the file formats. There is
no way to mark a dynamic array formula in the XLS file format.
X (✕) marks features that are not supported by the file formats. There is no way
to mark a dynamic array formula in the XLS file format.
</details>
@ -163,7 +163,7 @@ function ConcatFormula(props) {
};
return ( <>
<input type="file" onChange={process_file}/><br/>
<b>addr: </b><input type="text" value={addr} onChange={setaddr} size="6"/>
<b>Cell: </b><input type="text" value={addr} onChange={setaddr} size="6"/>
{!ws[addr] ? ( <b>Cell {addr} not found</b> ) : ( <table>
<tr><td>Formula</td><td><code>{ws[addr].f}</code></td></tr>
<tr><td>Value</td><td><code>{ws[addr].v}</code></td></tr>
@ -201,6 +201,8 @@ var worksheet = XLSX.utils.aoa_to_sheet([
<details open><summary><b>Live Example</b> (click to hide)</summary>
This demo creates a worksheet where `A1=1`, `A2=2`, and `A3=A1+A2`.
```jsx live
/* The live editor requires this function wrapper */
function ExportSimpleFormula(props) {
@ -238,10 +240,13 @@ var worksheet = XLSX.utils.aoa_to_sheet([
])
```
:::note pass
If the actual results are needed in JS, [SheetJS Pro](https://sheetjs.com/pro)
offers a formula calculator component for evaluating expressions, updating
values and dependent cells, and refreshing entire workbooks.
:::
## Array Formulae

@ -1,5 +1,5 @@
---
sidebar_position: 2
sidebar_position: 3
---
# Hyperlinks
@ -124,7 +124,7 @@ function SheetJSParseLinks(props) {
## Remote Links
HTTP / HTTPS links can be used directly:
HTTP and HTTPS links can be used directly:
```js
ws["A2"].l = { Target: "https://docs.sheetjs.com/docs/csf/features/hyperlinks" };
@ -208,6 +208,14 @@ ws["C3"].l = { Target: "#SheetJSDName" }; /* Link to Defined Name */
<details><summary><b>Live Example</b> (click to show)</summary>
This demo creates a workbook with two worksheets. In the first worksheet:
- Cell `A1` ("Same") will link to the range `B2:D4` in the first sheet
- Cell `B1` ("Cross") will link to the range `B2:D4` in the second sheet
- Cell `C1` ("Name") will link to the range in the defined name `SheetJSDN`
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={() => {
@ -248,10 +256,13 @@ XLSX documents. A workaround was added in library version 0.18.12.
## HTML
The HTML DOM parser will process `<a>` links in the table:
The HTML DOM parser[^1] will process `<a>` links in the table.
<details open><summary><b>Live Example</b> (click to hide)</summary>
This example uses `table_to_book` to generate a SheetJS workbook object from a
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) {
@ -278,3 +289,37 @@ function ExportHyperlink(props) {
```
</details>
The HTML writer[^2] will generate `<a>` links.
<details open><summary><b>Live Example</b> (click to hide)</summary>
This example creates a worksheet where `A1` has a link and `B1` does not. The
`sheet_to_html` function generates an HTML table where the topleft table cell
has a standard HTML link.
```jsx live
/* The live editor requires this function wrapper */
function ExportALinks(props) {
const [ __html, setHTML ] = React.useState("");
React.useEffect(() => {
/* Create worksheet */
var ws = XLSX.utils.aoa_to_sheet([ [ "Link", "No Link" ] ]);
/* Add link */
ws["A1"].l = {
Target: "https://sheetjs.com",
Tooltip: "Find us @ SheetJS.com!"
};
/* Generate HTML */
setHTML(XLSX.utils.sheet_to_html(ws));
}, []);
return ( <div dangerouslySetInnerHTML={{__html}}/> );
}
```
</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)

@ -167,7 +167,6 @@ function SheetJSComments2() {
XLSX.writeFile(wb, "SheetJSComments2.xlsx");
}}>Click me to generate a sample file</button>);
}
```
</details>

@ -0,0 +1,150 @@
---
title: Sheet Visibility
sidebar_position: 7
---
<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"
state is controlled through the "Visibility" property in the VBA editor.
| Formats | Hidden | Very Hidden |
|:----------|:------:|:-----------:|
| XLSX/XLSM | ✔ | ✔ |
| XLSB | ✔ | ✔ |
| XLML | ✔ | ✔ |
| BIFF8 XLS | ✔ | ✔ |
| BIFF5 XLS | ✔ | ✔ |
</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.
Standard "hidden" sheets are revealed 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!
## Storage
The visibility setting is stored in the `Hidden` property of the corresponding
[metadata in the `wb.Workbook.Sheets` array](/docs/csf/book#sheet-metadata)
The recognized values are listed below:
| Value | Definition | VB Editor "Visible" Property |
|:-----:|:------------|:-----------------------------|
| 0 | Visible | `-1 - xlSheetVisible` |
| 1 | Hidden | ` 0 - xlSheetHidden` |
| 2 | Very Hidden | ` 2 - xlSheetVeryHidden` |
If the respective Sheet entry does not exist or if the `Hidden` property is not
set, the worksheet is visible.
### Parsing
Since worksheet visibility is stored in the workbook, both the workbook object
and the sheet name must be known to determine visibility setting.
```js
function get_sheet_visibility(workbook, sheet_name) {
// if the metadata does not exist for the sheet, the sheet is visible
if(!workbook.Workbook) return 0;
if(!workbook.Workbook.Sheets) return 0;
var idx = workbook.SheetNames.indexOf(sheet_name);
if(idx == -1) throw new Error(`Sheet ${sheet_name} missing from workbook`);
var meta = workbook.Workbook.Sheets[idx];
return meta && meta.Hidden || 0;
}
```
Typically the distinction between "hidden" and "very hidden" is not relevant for
applications. The values were chosen to make logical negation work as expected:
```js
function is_sheet_visible(workbook, sheet_name) {
return !get_sheet_visibility(workbook, sheet_name); // true if visible
}
```
### Writing
When assigning, the entire workbook metadata structure should be tested and
constructed if necessary:
```js
function set_sheet_visibility(workbook, sheet_name, Hidden) {
var idx = workbook.SheetNames.indexOf(sheet_name);
if(idx == -1) throw new Error(`Sheet ${sheet_name} missing from workbook`);
// if the metadata does not exist for the sheet, create it
if(!workbook.Workbook) workbook.Workbook = {};
if(!workbook.Workbook.Sheets) workbook.Workbook.Sheets = [];
if(!workbook.Workbook.Sheets[idx]) workbook.Workbook.Sheets[idx] = {};
// set visibility
workbook.Workbook.Sheets[idx].Hidden = Hidden;
}
```
## Demo
[This test file](pathname:///files/sheet_visibility.xlsx) has three sheets:
- "Visible" is visible
- "Hidden" is hidden
- "VeryHidden" is very hidden
![Screenshot](pathname:///files/sheet_visibility.png)
The live demo fetches the test file and displays visibility information.
```jsx live
function Visibility(props) {
const [wb, setWB] = React.useState({SheetNames:[]});
const [sheets, setSheets] = React.useState([]);
const vis = [ "Visible", "Hidden", "Very Hidden" ];
React.useEffect(async() => {
const f = await fetch("/files/sheet_visibility.xlsx");
const ab = await f.arrayBuffer();
const wb = XLSX.read(ab);
setWB(wb);
/* State will be set to the `Sheets` property array */
setSheets(wb.Workbook.Sheets);
}, []);
return (<table>
<thead><tr><th>Name</th><th>Value</th><th>Hidden</th></tr></thead>
<tbody>{wb.SheetNames.map((n,i) => {
const h = ((((wb||{}).Workbook||{}).Sheets||[])[i]||{}).Hidden||0;
return ( <tr key={i}>
<td>{n}</td>
<td>{h} - {vis[h]}</td>
<td>{!h ? "No" : "Yes"}</td>
</tr> );
})}</tbody></table>);
}
```
:::info pass
The live codeblock tests for visibility with:
```js
const h = ((((wb||{}).Workbook||{}).Sheets||[])[i]||{}).Hidden||0;
```
With modern JS, this can be written as
```js
const h = wb?.Workbook?.Sheets?.[i]?.Hidden||0;
```
:::

@ -138,91 +138,3 @@ follow the priority order:
3) use `wch` character count if available
</details>
## Sheet Visibility
<details>
<summary><b>Format Support</b> (click to show)</summary>
**Hidden Sheets**: XLSX/M, XLSB, BIFF8/BIFF5 XLS, XLML
**Very Hidden Sheets**: XLSX/M, XLSB, BIFF8/BIFF5 XLS, XLML
</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. Standard hidden sheets
are revealed in the "Unhide" menu. Excel also has "very hidden" sheets which
cannot be revealed in the menu. It is only accessible in the VB Editor!
The visibility setting is stored in the `Hidden` property of sheet props array.
| Value | Definition | VB Editor "Visible" Property |
|:-----:|:------------|:-----------------------------|
| 0 | Visible | `-1 - xlSheetVisible` |
| 1 | Hidden | ` 0 - xlSheetHidden` |
| 2 | Very Hidden | ` 2 - xlSheetVeryHidden` |
If the respective Sheet entry does not exist or if the `Hidden` property is not
set, the worksheet is visible.
**List all worksheets and their visibility settings**
```js
wb.Workbook.Sheets.map(function(x) { return [x.name, x.Hidden] })
// [ [ 'Visible', 0 ], [ 'Hidden', 1 ], [ 'VeryHidden', 2 ] ]
```
**Check if worksheet is visible**
Non-Excel formats do not support the Very Hidden state. The best way to test
if a sheet is visible is to check if the `Hidden` property is logical truth:
```js
wb.Workbook.Sheets.map(function(x) { return [x.name, !x.Hidden] })
// [ [ 'Visible', true ], [ 'Hidden', false ], [ 'VeryHidden', false ] ]
```
<details>
<summary><b>Live Example</b> (click to show)</summary>
[This test file](pathname:///files/sheet_visibility.xlsx) has three sheets:
- "Visible" is visible
- "Hidden" is hidden
- "VeryHidden" is very hidden
![Screenshot](pathname:///files/sheet_visibility.png)
**Live demo**
```jsx live
function Visibility(props) {
const [sheets, setSheets] = React.useState([]);
const names = [ "Visible", "Hidden", "Very Hidden" ];
React.useEffect(async() => {
const f = await fetch("/files/sheet_visibility.xlsx");
const ab = await f.arrayBuffer();
const wb = XLSX.read(ab);
/* State will be set to the `Sheets` property array */
setSheets(wb.Workbook.Sheets);
}, []);
return (<table>
<thead><tr><th>Name</th><th>Value</th><th>Hidden</th></tr></thead>
<tbody>{sheets.map((x,i) => (<tr key={i}>
<td>{x.name}</td>
<td>{x.Hidden} - {names[x.Hidden]}</td>
<td>{!x.Hidden ? "No" : "Yes"}</td>
</tr>))}</tbody></table>);
}
```
</details>

@ -0,0 +1,31 @@
import { writeFile, set_fs, utils } from 'xlsx';
import * as fs from 'fs'; set_fs(fs);
import { MongoClient } from 'mongodb';
const url = 'mongodb://localhost:27017/sheetjs';
const db_name = 'sheetjs';
/* Connect to mongodb server */
const client = await MongoClient.connect(url, { useUnifiedTopology: true });
/* Sample data table */
const db = client.db(db_name);
try { await db.collection('pres').drop(); } catch(e) {}
const pres = db.collection('pres');
await pres.insertMany([
{ name: "Barack Obama", idx: 44 },
{ name: "Donald Trump", idx: 45 },
{ name: "Joseph Biden", idx: 46 }
], {ordered: true});
/* Create worksheet from collection */
const aoo = await pres.find({}, {projection:{_id:0}}).toArray();
const ws = utils.json_to_sheet(aoo);
/* Export to XLSX */
const wb = utils.book_new();
utils.book_append_sheet(wb, ws, "Presidents");
writeFile(wb, "SheetJSMongoCRUD.xlsx");
/* Close connection */
client.close();