react-native-nitro-xlsx 0.1.2 → 0.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NOTICE +8 -35
- package/NitroXlsx.podspec +21 -57
- package/Package.swift +35 -0
- package/README.md +243 -36
- package/android/CMakeLists.txt +17 -23
- package/android/build.gradle +6 -0
- package/android/cpp-adapter.cpp +12 -0
- package/android/src/main/java/com/margelo/nitro/xlsx/NitroXlsxPackage.kt +9 -0
- package/cpp/HybridNitroXlsx.cpp +32 -9
- package/cpp/HybridNitroXlsx.hpp +2 -1
- package/cpp/HybridXlsxConditionalFormat.cpp +252 -0
- package/cpp/HybridXlsxConditionalFormat.hpp +62 -0
- package/cpp/HybridXlsxConditionalFormats.cpp +46 -0
- package/cpp/HybridXlsxConditionalFormats.hpp +28 -0
- package/cpp/HybridXlsxWorkbook.cpp +162 -87
- package/cpp/HybridXlsxWorkbook.hpp +7 -1
- package/cpp/HybridXlsxWorksheet.cpp +456 -61
- package/cpp/HybridXlsxWorksheet.hpp +73 -26
- package/cpp/XlsxCacheDir.cpp +76 -0
- package/cpp/XlsxCacheDir.hpp +27 -0
- package/cpp/XlsxError.hpp +68 -0
- package/cpp/XlsxJson.cpp +185 -0
- package/cpp/XlsxJson.hpp +36 -0
- package/ios/XlsxCacheDir.mm +60 -0
- package/nitrogen/generated/android/NitroXlsx+autolinking.cmake +2 -0
- package/nitrogen/generated/shared/c++/HybridNitroXlsxSpec.cpp +1 -0
- package/nitrogen/generated/shared/c++/HybridNitroXlsxSpec.hpp +5 -2
- package/nitrogen/generated/shared/c++/HybridXlsxConditionalFormatSpec.cpp +54 -0
- package/nitrogen/generated/shared/c++/HybridXlsxConditionalFormatSpec.hpp +95 -0
- package/nitrogen/generated/shared/c++/HybridXlsxConditionalFormatsSpec.cpp +24 -0
- package/nitrogen/generated/shared/c++/HybridXlsxConditionalFormatsSpec.hpp +68 -0
- package/nitrogen/generated/shared/c++/HybridXlsxWorkbookSpec.cpp +4 -0
- package/nitrogen/generated/shared/c++/HybridXlsxWorkbookSpec.hpp +7 -0
- package/nitrogen/generated/shared/c++/HybridXlsxWorksheetSpec.cpp +47 -19
- package/nitrogen/generated/shared/c++/HybridXlsxWorksheetSpec.hpp +52 -20
- package/package.json +5 -2
- package/scripts/prepare-openxlsx.js +126 -0
- package/scripts/run-cpp-tests.sh +14 -0
- package/src/__mocks__/react-native-nitro-modules.ts +1 -0
- package/src/__tests__/index.test.ts +76 -7
- package/src/index.ts +110 -9
- package/src/specs/NitroXlsx.nitro.ts +141 -42
package/NOTICE
CHANGED
|
@@ -4,7 +4,8 @@ This software uses third-party open source components listed below, with full li
|
|
|
4
4
|
================================================================================
|
|
5
5
|
1. OpenXLSX
|
|
6
6
|
Repository: https://github.com/troldal/OpenXLSX
|
|
7
|
-
|
|
7
|
+
Version: 0.5.1
|
|
8
|
+
License: BSD 3-Clause License (SPDX: BSD-3-Clause)
|
|
8
9
|
Copyright (c) 2020 Kenneth Troldal Balslev
|
|
9
10
|
|
|
10
11
|
Full BSD 3-Clause License text:
|
|
@@ -27,7 +28,7 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
|
27
28
|
2. react-native-nitro-modules
|
|
28
29
|
Repository: https://github.com/mrousavy/nitro
|
|
29
30
|
Version: 0.58.3
|
|
30
|
-
License: MIT License
|
|
31
|
+
License: MIT License (SPDX: MIT)
|
|
31
32
|
Copyright (c) 2024 Marc Rousavy
|
|
32
33
|
|
|
33
34
|
Full MIT License text:
|
|
@@ -56,7 +57,8 @@ SOFTWARE.
|
|
|
56
57
|
================================================================================
|
|
57
58
|
3. pugixml (OpenXLSX dependency)
|
|
58
59
|
Repository: https://github.com/zeux/pugixml
|
|
59
|
-
|
|
60
|
+
Version: 1.16
|
|
61
|
+
License: MIT License (SPDX: MIT)
|
|
60
62
|
Copyright (c) 2006-2024 Arseny Kapoulkine
|
|
61
63
|
|
|
62
64
|
Full MIT License text:
|
|
@@ -72,39 +74,10 @@ The above copyright notice and this permission notice shall be included in all c
|
|
|
72
74
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
73
75
|
|
|
74
76
|
================================================================================
|
|
75
|
-
4.
|
|
76
|
-
Repository: https://github.com/troldal/Zippy
|
|
77
|
-
License: MIT License
|
|
78
|
-
Copyright (c) 2019 Kenneth Troldal Balslev
|
|
79
|
-
|
|
80
|
-
Full MIT License text:
|
|
81
|
-
|
|
82
|
-
MIT License
|
|
83
|
-
|
|
84
|
-
Copyright (c) 2019 Kenneth Troldal Balslev
|
|
85
|
-
|
|
86
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
87
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
88
|
-
in the Software without restriction, including without limitation the rights
|
|
89
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
90
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
91
|
-
furnished to do so, subject to the following conditions:
|
|
92
|
-
|
|
93
|
-
The above copyright notice and this permission notice shall be included in all
|
|
94
|
-
copies or substantial portions of the Software.
|
|
95
|
-
|
|
96
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
97
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
98
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
99
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
100
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
101
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
102
|
-
SOFTWARE.
|
|
103
|
-
|
|
104
|
-
================================================================================
|
|
105
|
-
5. miniz (OpenXLSX dependency)
|
|
77
|
+
4. miniz (OpenXLSX dependency)
|
|
106
78
|
Repository: https://github.com/richgel999/miniz
|
|
107
|
-
|
|
79
|
+
Version: 3.1.2
|
|
80
|
+
License: MIT License (SPDX: MIT)
|
|
108
81
|
Copyright (c) 2013-2014 RAD Game Tools and Valve Software
|
|
109
82
|
Copyright (c) 2010-2014 Rich Geldreich and Tenacious Software LLC
|
|
110
83
|
|
package/NitroXlsx.podspec
CHANGED
|
@@ -11,12 +11,17 @@ Pod::Spec.new do |s|
|
|
|
11
11
|
s.authors = package["author"]
|
|
12
12
|
|
|
13
13
|
s.platforms = { :ios => "15.1" }
|
|
14
|
-
s.source = { :git => "", :tag => "#{s.version}" }
|
|
14
|
+
s.source = { :git => "https://github.com/zydbkqf/react-native-nitro-xlsx.git", :tag => "#{s.version}" }
|
|
15
15
|
|
|
16
|
-
s.exclude_files =
|
|
16
|
+
s.exclude_files = [
|
|
17
|
+
"cpp/tests/**/*",
|
|
18
|
+
"OpenXLSX/ThirdParty/miniz/tests/**/*",
|
|
19
|
+
"OpenXLSX/ThirdParty/miniz/examples/**/*",
|
|
20
|
+
]
|
|
17
21
|
|
|
18
22
|
s.source_files = [
|
|
19
23
|
"cpp/**/*.{h,hpp,cpp}",
|
|
24
|
+
"ios/**/*.{h,hpp,mm}",
|
|
20
25
|
"OpenXLSX/OpenXLSX/**/*.hpp",
|
|
21
26
|
"OpenXLSX/OpenXLSX/**/*.cpp",
|
|
22
27
|
"OpenXLSX/ThirdParty/pugixml/src/**/*.hpp",
|
|
@@ -49,61 +54,20 @@ Pod::Spec.new do |s|
|
|
|
49
54
|
"HEADER_SEARCH_PATHS" => "$(inherited) ${PODS_TARGET_SRCROOT}/OpenXLSX ${PODS_TARGET_SRCROOT}/OpenXLSX/OpenXLSX ${PODS_TARGET_SRCROOT}/OpenXLSX/OpenXLSX/headers ${PODS_TARGET_SRCROOT}/OpenXLSX/ThirdParty/pugixml/src ${PODS_TARGET_SRCROOT}/OpenXLSX/ThirdParty/miniz",
|
|
50
55
|
}
|
|
51
56
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
#define MINIZ_EXPORT_H
|
|
67
|
-
|
|
68
|
-
#ifndef MINIZ_EXPORT
|
|
69
|
-
#define MINIZ_EXPORT
|
|
70
|
-
#endif
|
|
71
|
-
|
|
72
|
-
#ifndef MINIZ_DEPRECATED
|
|
73
|
-
#define MINIZ_DEPRECATED
|
|
74
|
-
#endif
|
|
75
|
-
|
|
76
|
-
#ifndef MINIZ_DEPRECATED_EXPORT
|
|
77
|
-
#define MINIZ_DEPRECATED_EXPORT MINIZ_EXPORT MINIZ_DEPRECATED
|
|
78
|
-
#endif
|
|
79
|
-
|
|
80
|
-
#ifndef MINIZ_NO_EXPORT
|
|
81
|
-
#define MINIZ_NO_EXPORT
|
|
82
|
-
#endif
|
|
83
|
-
|
|
84
|
-
#endif
|
|
85
|
-
EOF
|
|
86
|
-
fi
|
|
87
|
-
if [ ! -f "OpenXLSX/OpenXLSX/headers/OpenXLSX-Exports.hpp" ]; then
|
|
88
|
-
cat > OpenXLSX/OpenXLSX/headers/OpenXLSX-Exports.hpp << 'EOF'
|
|
89
|
-
#ifndef OPENXLSX_EXPORTS_HPP
|
|
90
|
-
#define OPENXLSX_EXPORTS_HPP
|
|
91
|
-
|
|
92
|
-
#ifndef OPENXLSX_EXPORT
|
|
93
|
-
#define OPENXLSX_EXPORT
|
|
94
|
-
#endif
|
|
95
|
-
|
|
96
|
-
#ifndef OPENXLSX_HIDDEN
|
|
97
|
-
#define OPENXLSX_HIDDEN
|
|
98
|
-
#endif
|
|
99
|
-
|
|
100
|
-
#endif
|
|
101
|
-
EOF
|
|
102
|
-
fi
|
|
103
|
-
if [ ! -d "OpenXLSX/OpenXLSX/headers/OpenXLSX" ]; then
|
|
104
|
-
cd OpenXLSX/OpenXLSX/headers && ln -sf . OpenXLSX && cd -
|
|
105
|
-
fi
|
|
106
|
-
CMD
|
|
57
|
+
# Prepare OpenXLSX and its dependencies (pugixml, miniz) via a Node script
|
|
58
|
+
# so the same logic can be reused from both CocoaPods and Android CMake.
|
|
59
|
+
s.prepare_command = "node #{__dir__}/scripts/prepare-openxlsx.js"
|
|
60
|
+
|
|
61
|
+
# Swift Package Manager declaration for React Native 0.87+.
|
|
62
|
+
# This exposes NitroXlsx as a local Swift package; the heavy lifting for
|
|
63
|
+
# OpenXLSX is still done by the prepare command above.
|
|
64
|
+
if ENV['NITRO_XLSX_USE_SPM'] == '1' && defined?(SPM) && SPM.respond_to?(:dependency)
|
|
65
|
+
SPM.dependency(s,
|
|
66
|
+
url: File.join(__dir__, "."),
|
|
67
|
+
requirement: { kind: 'upToNextMajorVersion', minimumVersion: s.version.to_s },
|
|
68
|
+
products: ['NitroXlsx']
|
|
69
|
+
)
|
|
70
|
+
end
|
|
107
71
|
|
|
108
72
|
load 'nitrogen/generated/ios/NitroXlsx+autolinking.rb'
|
|
109
73
|
add_nitrogen_files(s)
|
package/Package.swift
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// swift-tools-version: 5.9
|
|
2
|
+
import PackageDescription
|
|
3
|
+
|
|
4
|
+
// NOTE: This Package.swift is a Swift Package Manager manifest stub for
|
|
5
|
+
// React Native 0.87+ compatibility. The library is still primarily built
|
|
6
|
+
// through CocoaPods (NitroXlsx.podspec), where OpenXLSX, pugixml and miniz
|
|
7
|
+
// are cloned and compiled from source by scripts/prepare-openxlsx.js.
|
|
8
|
+
//
|
|
9
|
+
// The local package reference below lets RN 0.87's spm_dependency helper
|
|
10
|
+
// register NitroXlsx in the Pods project when SwiftPM mode is enabled.
|
|
11
|
+
// Full SPM-based compilation of the C++ sources is not yet supported —
|
|
12
|
+
// ios/XlsxCacheDir.mm is listed so the target has a real source file.
|
|
13
|
+
|
|
14
|
+
let package = Package(
|
|
15
|
+
name: "NitroXlsx",
|
|
16
|
+
platforms: [
|
|
17
|
+
.iOS(.v15)
|
|
18
|
+
],
|
|
19
|
+
products: [
|
|
20
|
+
.library(
|
|
21
|
+
name: "NitroXlsx",
|
|
22
|
+
targets: ["NitroXlsx"]
|
|
23
|
+
)
|
|
24
|
+
],
|
|
25
|
+
targets: [
|
|
26
|
+
.target(
|
|
27
|
+
name: "NitroXlsx",
|
|
28
|
+
path: "ios",
|
|
29
|
+
sources: ["XlsxCacheDir.mm"],
|
|
30
|
+
cxxSettings: [
|
|
31
|
+
.headerSearchPath("../cpp")
|
|
32
|
+
]
|
|
33
|
+
)
|
|
34
|
+
]
|
|
35
|
+
)
|
package/README.md
CHANGED
|
@@ -6,12 +6,19 @@ A React Native module for reading and writing Excel XLSX files using [OpenXLSX](
|
|
|
6
6
|
|
|
7
7
|
- 📊 Full Excel XLSX file creation support
|
|
8
8
|
- 📖 Read existing XLSX files (from file path or buffer)
|
|
9
|
-
- 📝 Formula support (with default values)
|
|
10
|
-
- 📅 Date/DateTime support
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
- 🔒 Sheet protection
|
|
9
|
+
- 📝 Formula support (with default values) & array formulas
|
|
10
|
+
- 📅 Date / DateTime support
|
|
11
|
+
- 🔗 Hyperlinks (URLs)
|
|
12
|
+
- 🔄 JSON import / export (`fromJSON` / `toJSON`)
|
|
13
|
+
- 🎨 Rich formatting (fonts, colors, borders, alignment, number formats)
|
|
14
|
+
- 🔒 Sheet protection (password, objects/scenarios, fine-grained allow/deny)
|
|
15
|
+
- 📋 Cell comments (get / set / delete, multi-author)
|
|
16
|
+
- 🔤 Conditional formatting rules
|
|
17
|
+
- 🗂️ Sheet management (delete / rename / clone)
|
|
18
|
+
- ➕ Row/column delete, merge / unmerge
|
|
19
|
+
- 🧩 Hidden rows/columns
|
|
20
|
+
- 🔍 Read cells (value, type, format, formula) + findCell
|
|
21
|
+
- ⚠️ Structured native error codes mapped to JS
|
|
15
22
|
- 📦 Export as ArrayBuffer - no file system required!
|
|
16
23
|
|
|
17
24
|
## Installation
|
|
@@ -22,6 +29,8 @@ npm install react-native-nitro-xlsx
|
|
|
22
29
|
yarn add react-native-nitro-xlsx
|
|
23
30
|
```
|
|
24
31
|
|
|
32
|
+
> **Note**: Native dependencies (OpenXlsx) are downloaded from GitHub during the first build. Please make sure you have a stable network connection.
|
|
33
|
+
|
|
25
34
|
### iOS
|
|
26
35
|
|
|
27
36
|
```sh
|
|
@@ -122,13 +131,73 @@ const fontName = format.getFontName();
|
|
|
122
131
|
const fontSize = format.getFontSize();
|
|
123
132
|
```
|
|
124
133
|
|
|
134
|
+
### Working with JSON
|
|
135
|
+
|
|
136
|
+
Per-sheet conversion lives on `XlsxWorksheet`. Workbook-level conversion uses a
|
|
137
|
+
`Record` keyed by worksheet name.
|
|
138
|
+
|
|
139
|
+
#### Export a worksheet (`sheet.toJSON`)
|
|
140
|
+
|
|
141
|
+
Convert one worksheet's data to an array of records. By default, the first row is used as object keys.
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
const sheet = workbook.getWorksheetByName('Data');
|
|
145
|
+
|
|
146
|
+
// Use the first row as keys (default)
|
|
147
|
+
const rows = sheet.toJSON();
|
|
148
|
+
// Result: [{ "Name": "Alice", "Age": 30 }, { "Name": "Bob", "Age": 25 }]
|
|
149
|
+
|
|
150
|
+
// Provide custom keys
|
|
151
|
+
const rows2 = sheet.toJSON(['firstName', 'yearsOld']);
|
|
152
|
+
// Result: [{ "firstName": "Alice", "yearsOld": 30 }, ...]
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
#### Import into a worksheet (`sheet.fromJSON`)
|
|
156
|
+
|
|
157
|
+
Write an array of records into an existing worksheet. The first record's keys become the header row.
|
|
158
|
+
|
|
159
|
+
```typescript
|
|
160
|
+
const sheet = workbook.addWorksheet('Data');
|
|
161
|
+
sheet.fromJSON([
|
|
162
|
+
{ Name: 'Alice', Age: 30, Active: true },
|
|
163
|
+
{ Name: 'Bob', Age: 25, Active: false },
|
|
164
|
+
]);
|
|
165
|
+
// Row 1 (header): Active | Age | Name (keys are sorted alphabetically)
|
|
166
|
+
// Row 2: true | 30 | Alice
|
|
167
|
+
// Row 3: false | 25 | Bob
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
#### Export all sheets (`workbook.toJSON`)
|
|
171
|
+
|
|
172
|
+
Returns a `Record` keyed by worksheet name.
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
const all = workbook.toJSON();
|
|
176
|
+
// Result: { "Data": [{ Name: 'Alice', ... }], "Summary": [{ ... }] }
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
#### Create a workbook from multiple sheets (`NitroXlsx.fromJSON`)
|
|
180
|
+
|
|
181
|
+
Each key becomes a worksheet.
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
const workbook = NitroXlsx.fromJSON({
|
|
185
|
+
Data: [{ Name: 'Alice', Age: 30 }],
|
|
186
|
+
Summary: [{ Total: 1 }],
|
|
187
|
+
});
|
|
188
|
+
// Creates worksheets "Data" and "Summary"
|
|
189
|
+
|
|
190
|
+
const buffer = await workbook.getBuffer();
|
|
191
|
+
```
|
|
192
|
+
|
|
125
193
|
## API
|
|
126
194
|
|
|
127
195
|
### NitroXlsx
|
|
128
196
|
|
|
129
|
-
- `createWorkbook(
|
|
197
|
+
- `createWorkbook(): XlsxWorkbook` - Create a new workbook. The native side supplies the platform cache directory automatically (`Context.getCacheDir()` on Android, `NSCachesDirectory` on iOS).
|
|
130
198
|
- `openWorkbook(path: string): XlsxWorkbook` - Open workbook from file path
|
|
131
199
|
- `openWorkbookFromBuffer(buffer: ArrayBuffer): XlsxWorkbook` - Open workbook from buffer
|
|
200
|
+
- `fromJSON(data: Record<string, Array<Record<string, string | number | boolean | null>>>): XlsxWorkbook` - Create a workbook with one worksheet per key (native `AnyMap`s, no JSON round-trip)
|
|
132
201
|
|
|
133
202
|
### XlsxWorkbook
|
|
134
203
|
|
|
@@ -137,8 +206,12 @@ const fontSize = format.getFontSize();
|
|
|
137
206
|
- `getWorksheetByName(name: string): XlsxWorksheet` - Get worksheet by name
|
|
138
207
|
- `getOrAddWorksheet(name: string): XlsxWorksheet` - Get or add worksheet
|
|
139
208
|
- `getWorksheetCount(): number` - Get number of worksheets
|
|
209
|
+
- `deleteSheet(name: string): void` - Delete a worksheet by name
|
|
210
|
+
- `updateSheetName(oldName: string, newName: string): void` - Rename a worksheet and rewrite formula references to it
|
|
211
|
+
- `clone(existingName: string, newName: string): XlsxWorksheet` - Copy a worksheet
|
|
140
212
|
- `addCellFormat(): XlsxCellFormat` - Add a new cell format
|
|
141
213
|
- `getBuffer(): Promise<ArrayBuffer>` - Generate and return the XLSX file as a buffer
|
|
214
|
+
- `toJSON(): Record<string, Array<Record<string, string | number | boolean | null>>>` - Export every worksheet as a `Record` keyed by sheet name
|
|
142
215
|
|
|
143
216
|
### XlsxWorksheet
|
|
144
217
|
|
|
@@ -164,7 +237,7 @@ const fontSize = format.getFontSize();
|
|
|
164
237
|
#### Date/URL
|
|
165
238
|
| Method | Description |
|
|
166
239
|
|--------|-------------|
|
|
167
|
-
| `writeDatetime(row, col, datetime, format?)` | Write a datetime (Excel serial date number) |
|
|
240
|
+
| `writeDatetime(row, col, datetime, format?)` | Write a datetime (1900-based Excel serial date number; see Cell Types) |
|
|
168
241
|
| `writeURL(row, col, url, format?)` | Write a hyperlink |
|
|
169
242
|
|
|
170
243
|
#### Column/Row
|
|
@@ -172,6 +245,8 @@ const fontSize = format.getFontSize();
|
|
|
172
245
|
|--------|-------------|
|
|
173
246
|
| `setColumn(firstCol, lastCol, width, format?)` | Set column properties |
|
|
174
247
|
| `setRow(row, height, format?)` | Set row properties |
|
|
248
|
+
| `deleteRow(row)` | Delete a row (returns `true` if a row entry existed and was removed) |
|
|
249
|
+
| `deleteColumn(col)` | Delete a column and shift remaining columns left (returns `true` on success) |
|
|
175
250
|
| `setColumnHidden(firstCol, lastCol, hidden)` | Hide/show columns |
|
|
176
251
|
| `setRowHidden(row, hidden)` | Hide/show row |
|
|
177
252
|
|
|
@@ -180,38 +255,75 @@ const fontSize = format.getFontSize();
|
|
|
180
255
|
|--------|-------------|
|
|
181
256
|
| `mergeRange(firstRow, firstCol, lastRow, lastCol, value, format?)` | Merge cells with string value |
|
|
182
257
|
| `mergeRangeNum(firstRow, firstCol, lastRow, lastCol, value, format?)` | Merge cells with number value |
|
|
258
|
+
| `unmergeCells(firstRow, firstCol, lastRow, lastCol)` | Unmerge a previously merged range |
|
|
183
259
|
|
|
184
|
-
####
|
|
260
|
+
#### Comments
|
|
185
261
|
| Method | Description |
|
|
186
262
|
|--------|-------------|
|
|
187
|
-
| `
|
|
188
|
-
| `
|
|
263
|
+
| `setComment(row, col, text, authorId?)` | Set a cell comment (creates the comments part on first use) |
|
|
264
|
+
| `getComment(row, col)` | Get comment text (empty string if none) |
|
|
265
|
+
| `hasComment(row, col)` | Whether the cell has a comment |
|
|
266
|
+
| `deleteComment(row, col)` | Delete a cell comment (returns `true` if one existed) |
|
|
267
|
+
| `getCommentCount()` | Number of comments on this sheet |
|
|
268
|
+
| `addCommentAuthor(author)` | Register an author, returns its `authorId` |
|
|
269
|
+
| `getCommentAuthor(authorId)` | Get author name by id |
|
|
189
270
|
|
|
190
271
|
#### View
|
|
272
|
+
|
|
191
273
|
| Method | Description |
|
|
192
274
|
|--------|-------------|
|
|
193
|
-
| `freezePanes(row, col)` | Freeze panes |
|
|
194
|
-
| `splitPanes(row, col)` | Split panes |
|
|
195
|
-
| `setTabColor(color)` | Set tab color |
|
|
196
275
|
| `hide()` | Hide worksheet |
|
|
197
276
|
| `activate()` | Activate worksheet |
|
|
198
|
-
| `setFirstSheet()` | Set as first sheet |
|
|
199
|
-
| `protect(password?)` | Protect worksheet |
|
|
200
277
|
|
|
201
|
-
####
|
|
278
|
+
#### Sheet Protection
|
|
202
279
|
| Method | Description |
|
|
203
280
|
|--------|-------------|
|
|
204
|
-
| `
|
|
205
|
-
| `
|
|
206
|
-
| `
|
|
207
|
-
| `
|
|
208
|
-
| `
|
|
209
|
-
| `
|
|
210
|
-
| `
|
|
211
|
-
| `
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
281
|
+
| `protect(password?)` | Convenience: set optional password and enable sheet protection |
|
|
282
|
+
| `protectSheet(set?)` | Enable/disable sheet protection (`set` defaults to `true`) |
|
|
283
|
+
| `protectObjects(set?)` | Protect drawing objects |
|
|
284
|
+
| `protectScenarios(set?)` | Protect scenarios |
|
|
285
|
+
| `setPassword(password)` | Set password (hashed by Excel algorithm) |
|
|
286
|
+
| `setPasswordHash(hash)` | Set a pre-computed 4-digit hex password hash |
|
|
287
|
+
| `clearPassword()` | Remove the password attribute |
|
|
288
|
+
| `clearSheetProtection()` | Remove `<sheetProtection>` entirely |
|
|
289
|
+
|
|
290
|
+
Fine-grained permissions (only meaningful while `protectSheet` is on).
|
|
291
|
+
`allow*(set?)` defaults to `true`; `deny*()` is shorthand for `allow*(false)`.
|
|
292
|
+
|
|
293
|
+
| Allow | Deny | Effect when allowed |
|
|
294
|
+
|-------|------|---------------------|
|
|
295
|
+
| `allowInsertColumns(set?)` | `denyInsertColumns()` | User may insert columns |
|
|
296
|
+
| `allowInsertRows(set?)` | `denyInsertRows()` | User may insert rows |
|
|
297
|
+
| `allowDeleteColumns(set?)` | `denyDeleteColumns()` | User may delete columns |
|
|
298
|
+
| `allowDeleteRows(set?)` | `denyDeleteRows()` | User may delete rows |
|
|
299
|
+
| `allowSelectLockedCells(set?)` | `denySelectLockedCells()` | User may select locked cells |
|
|
300
|
+
| `allowSelectUnlockedCells(set?)` | `denySelectUnlockedCells()` | User may select unlocked cells |
|
|
301
|
+
|
|
302
|
+
Protection state getters:
|
|
303
|
+
|
|
304
|
+
| Method | Returns |
|
|
305
|
+
|--------|---------|
|
|
306
|
+
| `sheetProtected()` / `objectsProtected()` / `scenariosProtected()` | Whether the corresponding protection is on |
|
|
307
|
+
| `insertColumnsAllowed()` / `insertRowsAllowed()` | Whether insert is allowed despite protection |
|
|
308
|
+
| `deleteColumnsAllowed()` / `deleteRowsAllowed()` | Whether delete is allowed despite protection |
|
|
309
|
+
| `selectLockedCellsAllowed()` / `selectUnlockedCellsAllowed()` | Whether select is allowed despite protection |
|
|
310
|
+
| `passwordIsSet()` | Whether a password hash is present |
|
|
311
|
+
| `passwordHash()` | The stored password hash (empty if none) |
|
|
312
|
+
| `sheetProtectionSummary()` | Human-readable summary of the protection settings |
|
|
313
|
+
|
|
314
|
+
Example:
|
|
315
|
+
|
|
316
|
+
```typescript
|
|
317
|
+
const sheet = workbook.addWorksheet('Locked');
|
|
318
|
+
sheet.writeString(1, 1, 'Protected');
|
|
319
|
+
sheet.setPassword('s3cret');
|
|
320
|
+
sheet.protectSheet();
|
|
321
|
+
sheet.allowDeleteRows(); // users may still delete rows
|
|
322
|
+
sheet.denySelectLockedCells(); // users cannot select locked cells
|
|
323
|
+
console.log(sheet.sheetProtected()); // true
|
|
324
|
+
console.log(sheet.deleteRowsAllowed()); // true
|
|
325
|
+
console.log(sheet.selectLockedCellsAllowed()); // false
|
|
326
|
+
```
|
|
215
327
|
|
|
216
328
|
#### Read Methods
|
|
217
329
|
| Method | Description |
|
|
@@ -221,11 +333,62 @@ const fontSize = format.getFontSize();
|
|
|
221
333
|
| `getCellRawValue(row, col)` | Get raw cell value |
|
|
222
334
|
| `getCellType(row, col)` | Get cell type |
|
|
223
335
|
| `getCellFormat(row, col)` | Get cell format |
|
|
336
|
+
| `hasFormula(row, col)` | Whether the cell contains a formula |
|
|
337
|
+
| `formula(row, col)` | Get the cell's formula string (empty if none) |
|
|
338
|
+
| `findCell(row, col)` | Whether a cell exists at these coordinates (does **not** create it) |
|
|
224
339
|
| `getRowCount()` | Get total row count |
|
|
225
340
|
| `getColumnCount()` | Get total column count |
|
|
226
341
|
| `getLastRow()` | Get last row index |
|
|
227
342
|
| `getLastColumn()` | Get last column index |
|
|
228
343
|
| `getName()` | Get worksheet name |
|
|
344
|
+
| `toJSON(keys?)` | Export this worksheet as an array of records (`keys` defaults to first-row headers) |
|
|
345
|
+
| `fromJSON(data)` | Write an array of records into this worksheet (first row = header) |
|
|
346
|
+
|
|
347
|
+
#### Conditional Formatting
|
|
348
|
+
| Method | Description |
|
|
349
|
+
|--------|-------------|
|
|
350
|
+
| `getConditionalFormats()` | Get the worksheet's `XlsxConditionalFormats` collection |
|
|
351
|
+
|
|
352
|
+
##### XlsxConditionalFormats
|
|
353
|
+
| Method | Description |
|
|
354
|
+
|--------|-------------|
|
|
355
|
+
| `getCount()` | Number of `<conditionalFormatting>` entries |
|
|
356
|
+
| `create(sqref)` | Create a new entry for a range (e.g. `"A1:A10"`), returns `XlsxConditionalFormat` |
|
|
357
|
+
| `getByIndex(index)` | Get an entry by index |
|
|
358
|
+
| `summary()` | Debug summary string |
|
|
359
|
+
|
|
360
|
+
##### XlsxConditionalFormat
|
|
361
|
+
| Method | Description |
|
|
362
|
+
|--------|-------------|
|
|
363
|
+
| `getSqref()` / `setSqref(sqref)` | The range these rules apply to |
|
|
364
|
+
| `getRuleCount()` / `createRule()` | Manage `cfRule` entries (`createRule` returns the new index) |
|
|
365
|
+
| `getRuleType(index)` / `setRuleType(index, type)` | Rule type — use `CfType` constants |
|
|
366
|
+
| `getRuleDxfId(index)` / `setRuleDxfId(index, dxfId)` | Differential format id |
|
|
367
|
+
| `getRuleFormula(index, formulaIndex)` / `setRuleFormula(index, formulaIndex, formula)` | Rule formula (`formulaIndex` must be `0`; OpenXLSX supports one formula per rule) |
|
|
368
|
+
| `getRuleOperator(index)` / `setRuleOperator(index, op)` | Comparison operator — use `CfOperator` |
|
|
369
|
+
| `getRuleText(index)` / `setRuleText(index, text)` | Text for text-based rules |
|
|
370
|
+
| `getRulePriority(index)` / `setRulePriority(index, priority)` | Rule priority (1-based) |
|
|
371
|
+
| `getRuleStopIfTrue(index)` / `setRuleStopIfTrue(index, stop)` | Stop evaluating further rules |
|
|
372
|
+
| `getRuleTimePeriod(index)` / `setRuleTimePeriod(index, period)` | Time period — use `CfTimePeriod` |
|
|
373
|
+
| `getRuleRank(index)` / `setRuleRank(index, rank)` | Top/bottom N |
|
|
374
|
+
| `getRuleStdDev(index)` / `setRuleStdDev(index, stdDev)` | Standard deviations for aboveAverage |
|
|
375
|
+
| `getRuleAboveAverage` / `setRuleAboveAverage` | aboveAverage flag |
|
|
376
|
+
| `getRulePercent` / `setRulePercent` | percent flag |
|
|
377
|
+
| `getRuleBottom` / `setRuleBottom` | bottom flag |
|
|
378
|
+
| `getRuleEqualAverage` / `setRuleEqualAverage` | equalAverage flag |
|
|
379
|
+
| `summary()` | Debug summary string |
|
|
380
|
+
|
|
381
|
+
Example:
|
|
382
|
+
|
|
383
|
+
```typescript
|
|
384
|
+
const cf = sheet.getConditionalFormats().create('B2:B100');
|
|
385
|
+
const ruleIndex = cf.createRule();
|
|
386
|
+
cf.setRuleType(ruleIndex, CfType.CELL_IS);
|
|
387
|
+
cf.setRuleOperator(ruleIndex, CfOperator.GREATER_THAN);
|
|
388
|
+
cf.setRuleFormula(ruleIndex, 0, '100');
|
|
389
|
+
cf.setRuleDxfId(ruleIndex, 0);
|
|
390
|
+
cf.setRulePriority(ruleIndex, 1);
|
|
391
|
+
```
|
|
229
392
|
|
|
230
393
|
### XlsxCellFormat
|
|
231
394
|
|
|
@@ -275,9 +438,14 @@ When reading cells, the following types are returned:
|
|
|
275
438
|
|
|
276
439
|
- `empty` - Empty cell
|
|
277
440
|
- `string` - Text string
|
|
278
|
-
- `number` - Numeric value
|
|
441
|
+
- `number` - Numeric value. Dates are stored as **Excel serial numbers in the 1900 date system** (days since `1900-01-01`, where `1900-01-01` = `1`, including the spurious `1900-02-29`), and are read back as `number`:
|
|
442
|
+
```ts
|
|
443
|
+
// Excel serial (1900-based) → JS Date
|
|
444
|
+
const date = new Date(Math.round((serial - 25569) * 86400000));
|
|
445
|
+
// JS Date → Excel serial (1900-based)
|
|
446
|
+
const serial = date.getTime() / 86400000 + 25569;
|
|
447
|
+
```
|
|
279
448
|
- `boolean` - Boolean value
|
|
280
|
-
- `date` - Date/time value
|
|
281
449
|
- `error` - Error value
|
|
282
450
|
- `formula` - Formula
|
|
283
451
|
- `blank` - Blank cell
|
|
@@ -296,7 +464,43 @@ The library exports several useful constants:
|
|
|
296
464
|
- `Gridlines` - Gridline options
|
|
297
465
|
- `Colors` - Common RGB colors
|
|
298
466
|
- `NumFormat` - Built-in number format indices
|
|
299
|
-
- `
|
|
467
|
+
- `CfType` - Conditional formatting rule types
|
|
468
|
+
- `CfOperator` - Conditional formatting comparison operators
|
|
469
|
+
- `CfTimePeriod` - Conditional formatting time periods
|
|
470
|
+
- `XlsxErrorCode` - Native error codes
|
|
471
|
+
|
|
472
|
+
## Error Handling
|
|
473
|
+
|
|
474
|
+
Native failures are thrown as JS `Error`s whose message is `methodName: CODE: detail`.
|
|
475
|
+
Convert them to a structured `XlsxError` with `XlsxError.from`:
|
|
476
|
+
|
|
477
|
+
```typescript
|
|
478
|
+
import { NitroXlsx, XlsxError, XlsxErrorCode } from 'react-native-nitro-xlsx';
|
|
479
|
+
|
|
480
|
+
try {
|
|
481
|
+
workbook.deleteSheet('Missing');
|
|
482
|
+
} catch (e) {
|
|
483
|
+
const err = XlsxError.from(e);
|
|
484
|
+
if (err.code === XlsxErrorCode.SHEET_NOT_FOUND) {
|
|
485
|
+
// handle missing sheet
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
Error codes:
|
|
491
|
+
|
|
492
|
+
| Code | Meaning |
|
|
493
|
+
|------|---------|
|
|
494
|
+
| `INVALID_ARGUMENT` | Bad row/col/name or other input |
|
|
495
|
+
| `SHEET_NOT_FOUND` | Worksheet does not exist |
|
|
496
|
+
| `SHEET_EXISTS` | Worksheet name already taken |
|
|
497
|
+
| `INDEX_OUT_OF_RANGE` | Row/col/index out of range |
|
|
498
|
+
| `CELL_NOT_FOUND` | Referenced cell does not exist |
|
|
499
|
+
| `WORKBOOK_CLOSED` | Workbook already finalized via `getBuffer()` |
|
|
500
|
+
| `IO_ERROR` | File open/save failure |
|
|
501
|
+
| `UNSUPPORTED` | Operation not supported by the underlying engine |
|
|
502
|
+
| `XLSX_ERROR` | Generic OpenXLSX failure |
|
|
503
|
+
| `INTERNAL_ERROR` | Unexpected native error |
|
|
300
504
|
|
|
301
505
|
## Development
|
|
302
506
|
|
|
@@ -328,17 +532,20 @@ This library is based on OpenXLSX and inherits the following limitations:
|
|
|
328
532
|
|
|
329
533
|
The following Excel features are not supported and will be ignored when reading or writing files:
|
|
330
534
|
|
|
331
|
-
- 🖼️ **Images**: Reading and writing images is not supported. Existing images in XLSX files will be ignored.
|
|
332
535
|
- 📊 **Charts**: Charts, graphs, and sparklines are not supported.
|
|
333
|
-
-
|
|
536
|
+
- 🖼️ **Drawings/Images/Shapes**: Images, shapes, lines, arrows and other drawing objects fall under OpenXLSX's drawing support, which is not implemented. Existing images in XLSX files are silently ignored.
|
|
334
537
|
- 🔍 **Pivot Tables**: Pivot tables and pivot charts are not supported.
|
|
335
538
|
- 💻 **VBA/Macros**: VBA macros, forms, and ActiveX controls are not supported.
|
|
336
|
-
- 📋 **Comments**: Cell comments and notes are not supported.
|
|
337
539
|
- 📌 **Hyperlinks**: Only basic URL hyperlinks are supported.
|
|
338
|
-
- 🔄 **Conditional Formatting**: Conditional formatting rules are not supported.
|
|
339
540
|
- 📑 **Data Validation**: Data validation rules are not supported.
|
|
340
541
|
|
|
341
542
|
### Notes
|
|
342
543
|
|
|
343
544
|
- When reading XLSX files that contain unsupported features, the library will still read the cell data and formatting, but unsupported elements will be silently ignored.
|
|
344
|
-
- When writing XLSX files, unsupported features cannot be added and will be omitted from the output.
|
|
545
|
+
- When writing XLSX files, unsupported features cannot be added and will be omitted from the output.
|
|
546
|
+
- `deleteColumn` shifts cell values and formats left; formula references are not rewritten (same limitation as OpenXLSX's `deleteRow`).
|
|
547
|
+
- Conditional formatting supports one formula per `cfRule` (OpenXLSX limitation). `colorScale` / `dataBar` / `iconSet` are not supported.
|
|
548
|
+
- `deleteRule` on `XlsxConditionalFormat` always returns `false` (OpenXLSX does not support removing a `cfRule`).
|
|
549
|
+
- Conditional formatting rule `type` / `operator` / `timePeriod` are direct numeric mappings of OpenXLSX enums; passing an out-of-range value results in undefined behavior. Use the exported `CfType` / `CfOperator` / `CfTimePeriod` constants.
|
|
550
|
+
- `setComment` automatically registers an author so the generated `comments.xml` always has a valid `authorId` (verified to open cleanly in Excel/WPS).
|
|
551
|
+
- `getComment` returns an empty string for a cell without a comment (it does not throw). Use `hasComment` first if you need to distinguish.
|
package/android/CMakeLists.txt
CHANGED
|
@@ -5,22 +5,21 @@ set(CMAKE_CXX_STANDARD 20)
|
|
|
5
5
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
6
6
|
|
|
7
7
|
set(OPENXLSX_DIR ${CMAKE_SOURCE_DIR}/../OpenXLSX)
|
|
8
|
-
if(NOT EXISTS ${OPENXLSX_DIR}/CMakeLists.txt)
|
|
9
|
-
# Clean up incomplete directory to ensure fresh clone
|
|
10
|
-
if(EXISTS ${OPENXLSX_DIR})
|
|
11
|
-
file(REMOVE_RECURSE ${OPENXLSX_DIR})
|
|
12
|
-
endif()
|
|
13
8
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
9
|
+
# Prepare OpenXLSX and its dependencies (pugixml, miniz) using the same
|
|
10
|
+
# Node script that CocoaPods' prepare_command runs on iOS.
|
|
11
|
+
find_program(NODE_EXECUTABLE node REQUIRED)
|
|
12
|
+
execute_process(
|
|
13
|
+
COMMAND ${NODE_EXECUTABLE} ${CMAKE_SOURCE_DIR}/../scripts/prepare-openxlsx.js
|
|
14
|
+
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/..
|
|
15
|
+
RESULT_VARIABLE PREPARE_RESULT
|
|
16
|
+
)
|
|
17
|
+
if(NOT PREPARE_RESULT EQUAL 0)
|
|
18
|
+
message(FATAL_ERROR
|
|
19
|
+
"Failed to prepare OpenXLSX dependencies.\n"
|
|
20
|
+
"Please ensure Node.js is installed and run:\n"
|
|
21
|
+
" node scripts/prepare-openxlsx.js"
|
|
22
22
|
)
|
|
23
|
-
FetchContent_Populate(OpenXLSX)
|
|
24
23
|
endif()
|
|
25
24
|
|
|
26
25
|
# Verify OpenXLSX is available
|
|
@@ -33,15 +32,6 @@ if(NOT EXISTS ${OPENXLSX_DIR}/CMakeLists.txt)
|
|
|
33
32
|
)
|
|
34
33
|
endif()
|
|
35
34
|
|
|
36
|
-
# Create symlink for #include <OpenXLSX/XXX.hpp> resolution
|
|
37
|
-
# Same as iOS podspec: cd OpenXLSX/OpenXLSX/headers && ln -sf . OpenXLSX
|
|
38
|
-
if(NOT EXISTS ${OPENXLSX_DIR}/OpenXLSX/headers/OpenXLSX)
|
|
39
|
-
execute_process(
|
|
40
|
-
COMMAND ${CMAKE_COMMAND} -E create_symlink . OpenXLSX
|
|
41
|
-
WORKING_DIRECTORY ${OPENXLSX_DIR}/OpenXLSX/headers
|
|
42
|
-
)
|
|
43
|
-
endif()
|
|
44
|
-
|
|
45
35
|
set(OPENXLSX_BUILD_SAMPLES OFF CACHE BOOL "" FORCE)
|
|
46
36
|
set(OPENXLSX_BUILD_TESTS OFF CACHE BOOL "" FORCE)
|
|
47
37
|
set(OPENXLSX_BUILD_BENCHMARKS OFF CACHE BOOL "" FORCE)
|
|
@@ -57,6 +47,10 @@ add_library(
|
|
|
57
47
|
../cpp/HybridXlsxWorkbook.cpp
|
|
58
48
|
../cpp/HybridXlsxWorksheet.cpp
|
|
59
49
|
../cpp/HybridXlsxCellFormat.cpp
|
|
50
|
+
../cpp/HybridXlsxConditionalFormat.cpp
|
|
51
|
+
../cpp/HybridXlsxConditionalFormats.cpp
|
|
52
|
+
../cpp/XlsxJson.cpp
|
|
53
|
+
../cpp/XlsxCacheDir.cpp
|
|
60
54
|
cpp-adapter.cpp
|
|
61
55
|
)
|
|
62
56
|
|
package/android/build.gradle
CHANGED
|
@@ -4,6 +4,11 @@ buildscript {
|
|
|
4
4
|
}
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
+
def reactNativeArchitectures() {
|
|
8
|
+
def value = rootProject.getProperties().get("reactNativeArchitectures")
|
|
9
|
+
return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
|
|
10
|
+
}
|
|
11
|
+
|
|
7
12
|
apply plugin: 'com.android.library'
|
|
8
13
|
apply plugin: 'kotlin-android'
|
|
9
14
|
|
|
@@ -16,6 +21,7 @@ android {
|
|
|
16
21
|
externalNativeBuild {
|
|
17
22
|
cmake {
|
|
18
23
|
arguments "-DANDROID_STL=c++_shared"
|
|
24
|
+
abiFilters (*reactNativeArchitectures())
|
|
19
25
|
}
|
|
20
26
|
}
|
|
21
27
|
}
|