Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
610b899f66 | ||
|
|
24fb87721f | ||
|
|
d3bc8b135c | ||
|
|
0f6d311de2 | ||
|
|
0c15ae194d | ||
|
|
20c4a596bf | ||
|
|
f07a69260f |
11
.github/workflows/npm.yml
vendored
11
.github/workflows/npm.yml
vendored
@@ -12,11 +12,7 @@ permissions:
|
|||||||
publish:
|
publish:
|
||||||
description: "Publish to npm"
|
description: "Publish to npm"
|
||||||
required: true
|
required: true
|
||||||
type: choice
|
type: boolean
|
||||||
options:
|
|
||||||
- yes
|
|
||||||
- no
|
|
||||||
default: "no"
|
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
working-directory: ./bindings/nodejs
|
working-directory: ./bindings/nodejs
|
||||||
@@ -437,10 +433,11 @@ jobs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
- name: Publish
|
- name: Publish
|
||||||
run: |
|
run: |
|
||||||
npm config set provenance true
|
echo "${{ github.event.inputs.publish }}"
|
||||||
if [ "${{ github.event.inputs.publish }}" = "yes" ]; then
|
if [ "${{ github.event.inputs.publish }}" = "true" ]; then
|
||||||
echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc
|
echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc
|
||||||
npm publish --access public
|
npm publish --access public
|
||||||
|
echo "Published to npm"
|
||||||
else
|
else
|
||||||
echo "Not a release, skipping publish"
|
echo "Not a release, skipping publish"
|
||||||
fi
|
fi
|
||||||
|
|||||||
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -448,7 +448,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ironcalc_nodejs"
|
name = "ironcalc_nodejs"
|
||||||
version = "0.3.0"
|
version = "0.3.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ironcalc",
|
"ironcalc",
|
||||||
"napi",
|
"napi",
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ impl Workbook {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the a list of defined names in the workbook with their scope
|
/// Returns the a list of defined names in the workbook with their scope
|
||||||
pub(crate) fn get_defined_names_with_scope(&self) -> Vec<(String, Option<u32>, String)> {
|
pub fn get_defined_names_with_scope(&self) -> Vec<(String, Option<u32>, String)> {
|
||||||
let sheet_id_index: Vec<u32> = self.worksheets.iter().map(|s| s.sheet_id).collect();
|
let sheet_id_index: Vec<u32> = self.worksheets.iter().map(|s| s.sheet_id).collect();
|
||||||
|
|
||||||
let defined_names = self
|
let defined_names = self
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
name = "ironcalc_nodejs"
|
name = "ironcalc_nodejs"
|
||||||
version = "0.3.0"
|
version = "0.3.1"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
crate-type = ["cdylib"]
|
crate-type = ["cdylib"]
|
||||||
|
|||||||
@@ -1,11 +1,20 @@
|
|||||||
import test from 'ava'
|
import test from 'ava'
|
||||||
|
|
||||||
import { Model } from '../index.js';
|
import { UserModel, Model } from '../index.js';
|
||||||
|
|
||||||
test('sum from native', (t) => {
|
test('User Model smoke test', (t) => {
|
||||||
const model = new Model("Workbook1", "en", "UTC");
|
const model = new UserModel("Workbook1", "en", "UTC");
|
||||||
|
|
||||||
model.setUserInput(0, 1, 1, "=1+1");
|
model.setUserInput(0, 1, 1, "=1+1");
|
||||||
t.is(model.getFormattedCellValue(0, 1, 1), '2');
|
t.is(model.getFormattedCellValue(0, 1, 1), '2');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
test('Raw API smoke test', (t) => {
|
||||||
|
const model = new Model("Workbook1", "en", "UTC");
|
||||||
|
|
||||||
|
model.setUserInput(0, 1, 1, "=1+1");
|
||||||
|
model.evaluate();
|
||||||
|
t.is(model.getFormattedCellValue(0, 1, 1), '2');
|
||||||
|
});
|
||||||
|
|
||||||
|
|||||||
BIN
bindings/nodejs/example.xlsx
Normal file
BIN
bindings/nodejs/example.xlsx
Normal file
Binary file not shown.
40
bindings/nodejs/index.d.ts
vendored
40
bindings/nodejs/index.d.ts
vendored
@@ -5,7 +5,45 @@
|
|||||||
|
|
||||||
export declare class Model {
|
export declare class Model {
|
||||||
constructor(name: string, locale: string, timezone: string)
|
constructor(name: string, locale: string, timezone: string)
|
||||||
static fromBytes(bytes: Uint8Array): Model
|
static fromXlsx(filePath: string, locale: string, tz: string): Model
|
||||||
|
static fromIcalc(fileName: string): Model
|
||||||
|
saveToXlsx(file: string): void
|
||||||
|
saveToIcalc(file: string): void
|
||||||
|
evaluate(): void
|
||||||
|
setUserInput(sheet: number, row: number, column: number, value: string): void
|
||||||
|
clearCellContents(sheet: number, row: number, column: number): void
|
||||||
|
getCellContent(sheet: number, row: number, column: number): string
|
||||||
|
getCellType(sheet: number, row: number, column: number): number
|
||||||
|
getFormattedCellValue(sheet: number, row: number, column: number): string
|
||||||
|
setCellStyle(sheet: number, row: number, column: number, style: unknown): void
|
||||||
|
getCellStyle(sheet: number, row: number, column: number): unknown
|
||||||
|
insertRows(sheet: number, row: number, rowCount: number): void
|
||||||
|
insertColumns(sheet: number, column: number, columnCount: number): void
|
||||||
|
deleteRows(sheet: number, row: number, rowCount: number): void
|
||||||
|
deleteColumns(sheet: number, column: number, columnCount: number): void
|
||||||
|
getColumnWidth(sheet: number, column: number): number
|
||||||
|
getRowHeight(sheet: number, row: number): number
|
||||||
|
setColumnWidth(sheet: number, column: number, width: number): void
|
||||||
|
setRowHeight(sheet: number, row: number, height: number): void
|
||||||
|
getFrozenColumnsCount(sheet: number): number
|
||||||
|
getFrozenRowsCount(sheet: number): number
|
||||||
|
setFrozenColumnsCount(sheet: number, columnCount: number): void
|
||||||
|
setFrozenRowsCount(sheet: number, rowCount: number): void
|
||||||
|
getWorksheetsProperties(): unknown
|
||||||
|
setSheetColor(sheet: number, color: string): void
|
||||||
|
addSheet(sheetName: string): void
|
||||||
|
newSheet(): void
|
||||||
|
deleteSheet(sheet: number): void
|
||||||
|
renameSheet(sheet: number, newName: string): void
|
||||||
|
getDefinedNameList(): unknown
|
||||||
|
newDefinedName(name: string, scope: number | undefined | null, formula: string): void
|
||||||
|
updateDefinedName(name: string, scope: number | undefined | null, newName: string, newScope: number | undefined | null, newFormula: string): void
|
||||||
|
deleteDefinedName(name: string, scope?: number | undefined | null): void
|
||||||
|
testPanic(): void
|
||||||
|
}
|
||||||
|
export declare class UserModel {
|
||||||
|
constructor(name: string, locale: string, timezone: string)
|
||||||
|
static fromBytes(bytes: Uint8Array): UserModel
|
||||||
canUndo(): boolean
|
canUndo(): boolean
|
||||||
canRedo(): boolean
|
canRedo(): boolean
|
||||||
pauseEvaluation(): void
|
pauseEvaluation(): void
|
||||||
|
|||||||
@@ -310,6 +310,7 @@ if (!nativeBinding) {
|
|||||||
throw new Error(`Failed to load native binding`)
|
throw new Error(`Failed to load native binding`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const { Model } = nativeBinding
|
const { Model, UserModel } = nativeBinding
|
||||||
|
|
||||||
module.exports.Model = Model
|
module.exports.Model = Model
|
||||||
|
module.exports.UserModel = UserModel
|
||||||
|
|||||||
@@ -1,11 +1,28 @@
|
|||||||
import { Model } from './index.js'
|
import { UserModel, Model } from './index.js'
|
||||||
|
|
||||||
const model = new Model("Workbook1", "en", "UTC");
|
|
||||||
|
|
||||||
model.setUserInput(0, 1, 1, "=1+1");
|
|
||||||
let t = model.getFormattedCellValue(0, 1, 1);
|
|
||||||
|
|
||||||
console.log('From native', t);
|
function testUserModel() {
|
||||||
|
const model = new UserModel("Workbook1", "en", "UTC");
|
||||||
|
|
||||||
let t2 = model.getCellStyle(0, 1, 1);
|
model.setUserInput(0, 1, 1, "=1+1");
|
||||||
console.log('From native', t2);
|
let t = model.getFormattedCellValue(0, 1, 1);
|
||||||
|
|
||||||
|
console.log('From native', t);
|
||||||
|
|
||||||
|
let t2 = model.getCellStyle(0, 1, 1);
|
||||||
|
console.log('From native', t2);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testModel() {
|
||||||
|
const model = Model.fromXlsx("example.xlsx", "en", "UTC");
|
||||||
|
const style = model.getCellStyle(0, 1, 6);
|
||||||
|
console.log(style);
|
||||||
|
|
||||||
|
const quantum = model.getFormattedCellValue(0, 14, 4);
|
||||||
|
console.log(quantum);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
testUserModel();
|
||||||
|
testModel();
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@ironcalc/nodejs",
|
"name": "@ironcalc/nodejs",
|
||||||
"version": "0.3.0",
|
"version": "0.3.1",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"types": "index.d.ts",
|
"types": "index.d.ts",
|
||||||
"napi": {
|
"napi": {
|
||||||
|
|||||||
@@ -1,623 +1,8 @@
|
|||||||
#![deny(clippy::all)]
|
|
||||||
|
|
||||||
use serde::Serialize;
|
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate napi_derive;
|
extern crate napi_derive;
|
||||||
|
|
||||||
use napi::{self, bindgen_prelude::*, JsUnknown, Result};
|
mod model;
|
||||||
|
mod user_model;
|
||||||
|
|
||||||
use ironcalc::base::{
|
pub use model::Model;
|
||||||
expressions::types::Area,
|
pub use user_model::UserModel;
|
||||||
types::{CellType, Style},
|
|
||||||
BorderArea, ClipboardData, UserModel as BaseModel,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Serialize)]
|
|
||||||
struct DefinedName {
|
|
||||||
name: String,
|
|
||||||
scope: Option<u32>,
|
|
||||||
formula: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_js_error(error: String) -> Error {
|
|
||||||
Error::new(Status::Unknown, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi]
|
|
||||||
pub struct Model {
|
|
||||||
model: BaseModel,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi]
|
|
||||||
impl Model {
|
|
||||||
#[napi(constructor)]
|
|
||||||
pub fn new(name: String, locale: String, timezone: String) -> Result<Self> {
|
|
||||||
let model = BaseModel::new_empty(&name, &locale, &timezone).map_err(to_js_error)?;
|
|
||||||
Ok(Self { model })
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(factory)]
|
|
||||||
pub fn from_bytes(bytes: &[u8]) -> Result<Model> {
|
|
||||||
let model = BaseModel::from_bytes(bytes).map_err(to_js_error)?;
|
|
||||||
Ok(Model { model })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn undo(&mut self) -> Result<()> {
|
|
||||||
self.model.undo().map_err(to_js_error)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn redo(&mut self) -> Result<()> {
|
|
||||||
self.model.redo().map_err(to_js_error)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "canUndo")]
|
|
||||||
pub fn can_undo(&self) -> bool {
|
|
||||||
self.model.can_undo()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "canRedo")]
|
|
||||||
pub fn can_redo(&self) -> bool {
|
|
||||||
self.model.can_redo()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "pauseEvaluation")]
|
|
||||||
pub fn pause_evaluation(&mut self) {
|
|
||||||
self.model.pause_evaluation()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "resumeEvaluation")]
|
|
||||||
pub fn resume_evaluation(&mut self) {
|
|
||||||
self.model.resume_evaluation()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn evaluate(&mut self) {
|
|
||||||
self.model.evaluate();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "flushSendQueue")]
|
|
||||||
pub fn flush_send_queue(&mut self) -> Vec<u8> {
|
|
||||||
self.model.flush_send_queue()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "applyExternalDiffs")]
|
|
||||||
pub fn apply_external_diffs(&mut self, diffs: &[u8]) -> Result<()> {
|
|
||||||
self.model.apply_external_diffs(diffs).map_err(to_js_error)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "getCellContent")]
|
|
||||||
pub fn get_cell_content(&self, sheet: u32, row: i32, column: i32) -> Result<String> {
|
|
||||||
self
|
|
||||||
.model
|
|
||||||
.get_cell_content(sheet, row, column)
|
|
||||||
.map_err(to_js_error)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "newSheet")]
|
|
||||||
pub fn new_sheet(&mut self) -> Result<()> {
|
|
||||||
self.model.new_sheet().map_err(to_js_error)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "deleteSheet")]
|
|
||||||
pub fn delete_sheet(&mut self, sheet: u32) -> Result<()> {
|
|
||||||
self.model.delete_sheet(sheet).map_err(to_js_error)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "hideSheet")]
|
|
||||||
pub fn hide_sheet(&mut self, sheet: u32) -> Result<()> {
|
|
||||||
self.model.hide_sheet(sheet).map_err(to_js_error)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "unhideSheet")]
|
|
||||||
pub fn unhide_sheet(&mut self, sheet: u32) -> Result<()> {
|
|
||||||
self.model.unhide_sheet(sheet).map_err(to_js_error)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "renameSheet")]
|
|
||||||
pub fn rename_sheet(&mut self, sheet: u32, name: String) -> Result<()> {
|
|
||||||
self.model.rename_sheet(sheet, &name).map_err(to_js_error)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "setSheetColor")]
|
|
||||||
pub fn set_sheet_color(&mut self, sheet: u32, color: String) -> Result<()> {
|
|
||||||
self
|
|
||||||
.model
|
|
||||||
.set_sheet_color(sheet, &color)
|
|
||||||
.map_err(to_js_error)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "rangeClearAll")]
|
|
||||||
pub fn range_clear_all(
|
|
||||||
&mut self,
|
|
||||||
sheet: u32,
|
|
||||||
start_row: i32,
|
|
||||||
start_column: i32,
|
|
||||||
end_row: i32,
|
|
||||||
end_column: i32,
|
|
||||||
) -> Result<()> {
|
|
||||||
let range = Area {
|
|
||||||
sheet,
|
|
||||||
row: start_row,
|
|
||||||
column: start_column,
|
|
||||||
width: end_column - start_column + 1,
|
|
||||||
height: end_row - start_row + 1,
|
|
||||||
};
|
|
||||||
self.model.range_clear_all(&range).map_err(to_js_error)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "rangeClearContents")]
|
|
||||||
pub fn range_clear_contents(
|
|
||||||
&mut self,
|
|
||||||
sheet: u32,
|
|
||||||
start_row: i32,
|
|
||||||
start_column: i32,
|
|
||||||
end_row: i32,
|
|
||||||
end_column: i32,
|
|
||||||
) -> Result<()> {
|
|
||||||
let range = Area {
|
|
||||||
sheet,
|
|
||||||
row: start_row,
|
|
||||||
column: start_column,
|
|
||||||
width: end_column - start_column + 1,
|
|
||||||
height: end_row - start_row + 1,
|
|
||||||
};
|
|
||||||
self.model.range_clear_contents(&range).map_err(to_js_error)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "insertRow")]
|
|
||||||
pub fn insert_row(&mut self, sheet: u32, row: i32) -> Result<()> {
|
|
||||||
self.model.insert_row(sheet, row).map_err(to_js_error)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "insertColumn")]
|
|
||||||
pub fn insert_column(&mut self, sheet: u32, column: i32) -> Result<()> {
|
|
||||||
self.model.insert_column(sheet, column).map_err(to_js_error)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "deleteRow")]
|
|
||||||
pub fn delete_row(&mut self, sheet: u32, row: i32) -> Result<()> {
|
|
||||||
self.model.delete_row(sheet, row).map_err(to_js_error)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "deleteColumn")]
|
|
||||||
pub fn delete_column(&mut self, sheet: u32, column: i32) -> Result<()> {
|
|
||||||
self.model.delete_column(sheet, column).map_err(to_js_error)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "setRowHeight")]
|
|
||||||
pub fn set_row_height(&mut self, sheet: u32, row: i32, height: f64) -> Result<()> {
|
|
||||||
self
|
|
||||||
.model
|
|
||||||
.set_row_height(sheet, row, height)
|
|
||||||
.map_err(to_js_error)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "setColumnWidth")]
|
|
||||||
pub fn set_column_width(&mut self, sheet: u32, column: i32, width: f64) -> Result<()> {
|
|
||||||
self
|
|
||||||
.model
|
|
||||||
.set_column_width(sheet, column, width)
|
|
||||||
.map_err(to_js_error)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "getRowHeight")]
|
|
||||||
pub fn get_row_height(&mut self, sheet: u32, row: i32) -> Result<f64> {
|
|
||||||
self.model.get_row_height(sheet, row).map_err(to_js_error)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "getColumnWidth")]
|
|
||||||
pub fn get_column_width(&mut self, sheet: u32, column: i32) -> Result<f64> {
|
|
||||||
self
|
|
||||||
.model
|
|
||||||
.get_column_width(sheet, column)
|
|
||||||
.map_err(to_js_error)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "setUserInput")]
|
|
||||||
pub fn set_user_input(&mut self, sheet: u32, row: i32, column: i32, input: String) -> Result<()> {
|
|
||||||
self
|
|
||||||
.model
|
|
||||||
.set_user_input(sheet, row, column, &input)
|
|
||||||
.map_err(to_js_error)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "getFormattedCellValue")]
|
|
||||||
pub fn get_formatted_cell_value(&self, sheet: u32, row: i32, column: i32) -> Result<String> {
|
|
||||||
self
|
|
||||||
.model
|
|
||||||
.get_formatted_cell_value(sheet, row, column)
|
|
||||||
.map_err(to_js_error)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "getFrozenRowsCount")]
|
|
||||||
pub fn get_frozen_rows_count(&self, sheet: u32) -> Result<i32> {
|
|
||||||
self.model.get_frozen_rows_count(sheet).map_err(to_js_error)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "getFrozenColumnsCount")]
|
|
||||||
pub fn get_frozen_columns_count(&self, sheet: u32) -> Result<i32> {
|
|
||||||
self
|
|
||||||
.model
|
|
||||||
.get_frozen_columns_count(sheet)
|
|
||||||
.map_err(to_js_error)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "setFrozenRowsCount")]
|
|
||||||
pub fn set_frozen_rows_count(&mut self, sheet: u32, count: i32) -> Result<()> {
|
|
||||||
self
|
|
||||||
.model
|
|
||||||
.set_frozen_rows_count(sheet, count)
|
|
||||||
.map_err(to_js_error)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "setFrozenColumnsCount")]
|
|
||||||
pub fn set_frozen_columns_count(&mut self, sheet: u32, count: i32) -> Result<()> {
|
|
||||||
self
|
|
||||||
.model
|
|
||||||
.set_frozen_columns_count(sheet, count)
|
|
||||||
.map_err(to_js_error)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "updateRangeStyle")]
|
|
||||||
pub fn update_range_style(
|
|
||||||
&mut self,
|
|
||||||
env: Env,
|
|
||||||
range: JsUnknown,
|
|
||||||
style_path: String,
|
|
||||||
value: String,
|
|
||||||
) -> Result<()> {
|
|
||||||
let range: Area = env
|
|
||||||
.from_js_value(range)
|
|
||||||
.map_err(|e| to_js_error(e.to_string()))?;
|
|
||||||
self
|
|
||||||
.model
|
|
||||||
.update_range_style(&range, &style_path, &value)
|
|
||||||
.map_err(to_js_error)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "getCellStyle")]
|
|
||||||
pub fn get_cell_style(
|
|
||||||
&mut self,
|
|
||||||
env: Env,
|
|
||||||
sheet: u32,
|
|
||||||
row: i32,
|
|
||||||
column: i32,
|
|
||||||
) -> Result<JsUnknown> {
|
|
||||||
let style = self
|
|
||||||
.model
|
|
||||||
.get_cell_style(sheet, row, column)
|
|
||||||
.map_err(to_js_error)?;
|
|
||||||
|
|
||||||
env
|
|
||||||
.to_js_value(&style)
|
|
||||||
.map_err(|e| to_js_error(e.to_string()))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "onPasteStyles")]
|
|
||||||
pub fn on_paste_styles(&mut self, env: Env, styles: JsUnknown) -> Result<()> {
|
|
||||||
let styles: &Vec<Vec<Style>> = &env
|
|
||||||
.from_js_value(styles)
|
|
||||||
.map_err(|e| to_js_error(e.to_string()))?;
|
|
||||||
self.model.on_paste_styles(styles).map_err(to_js_error)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "getCellType")]
|
|
||||||
pub fn get_cell_type(&self, sheet: u32, row: i32, column: i32) -> Result<i32> {
|
|
||||||
Ok(
|
|
||||||
match self
|
|
||||||
.model
|
|
||||||
.get_cell_type(sheet, row, column)
|
|
||||||
.map_err(to_js_error)?
|
|
||||||
{
|
|
||||||
CellType::Number => 1,
|
|
||||||
CellType::Text => 2,
|
|
||||||
CellType::LogicalValue => 4,
|
|
||||||
CellType::ErrorValue => 16,
|
|
||||||
CellType::Array => 64,
|
|
||||||
CellType::CompoundData => 128,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// I don't _think_ serializing to JsUnknown can't fail
|
|
||||||
// FIXME: Remove this clippy directive
|
|
||||||
#[napi(js_name = "getWorksheetsProperties")]
|
|
||||||
#[allow(clippy::unwrap_used)]
|
|
||||||
pub fn get_worksheets_properties(&self, env: Env) -> JsUnknown {
|
|
||||||
env
|
|
||||||
.to_js_value(&self.model.get_worksheets_properties())
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "getSelectedSheet")]
|
|
||||||
pub fn get_selected_sheet(&self) -> u32 {
|
|
||||||
self.model.get_selected_sheet()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "getSelectedCell")]
|
|
||||||
pub fn get_selected_cell(&self) -> Vec<i32> {
|
|
||||||
let (sheet, row, column) = self.model.get_selected_cell();
|
|
||||||
vec![sheet as i32, row, column]
|
|
||||||
}
|
|
||||||
|
|
||||||
// I don't _think_ serializing to JsUnknown can't fail
|
|
||||||
// FIXME: Remove this clippy directive
|
|
||||||
#[napi(js_name = "getSelectedView")]
|
|
||||||
#[allow(clippy::unwrap_used)]
|
|
||||||
pub fn get_selected_view(&self, env: Env) -> JsUnknown {
|
|
||||||
env.to_js_value(&self.model.get_selected_view()).unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "setSelectedSheet")]
|
|
||||||
pub fn set_selected_sheet(&mut self, sheet: u32) -> Result<()> {
|
|
||||||
self.model.set_selected_sheet(sheet).map_err(to_js_error)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "setSelectedCell")]
|
|
||||||
pub fn set_selected_cell(&mut self, row: i32, column: i32) -> Result<()> {
|
|
||||||
self
|
|
||||||
.model
|
|
||||||
.set_selected_cell(row, column)
|
|
||||||
.map_err(to_js_error)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "setSelectedRange")]
|
|
||||||
pub fn set_selected_range(
|
|
||||||
&mut self,
|
|
||||||
start_row: i32,
|
|
||||||
start_column: i32,
|
|
||||||
end_row: i32,
|
|
||||||
end_column: i32,
|
|
||||||
) -> Result<()> {
|
|
||||||
self
|
|
||||||
.model
|
|
||||||
.set_selected_range(start_row, start_column, end_row, end_column)
|
|
||||||
.map_err(to_js_error)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "setTopLeftVisibleCell")]
|
|
||||||
pub fn set_top_left_visible_cell(&mut self, top_row: i32, top_column: i32) -> Result<()> {
|
|
||||||
self
|
|
||||||
.model
|
|
||||||
.set_top_left_visible_cell(top_row, top_column)
|
|
||||||
.map_err(to_js_error)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "setShowGridLines")]
|
|
||||||
pub fn set_show_grid_lines(&mut self, sheet: u32, show_grid_lines: bool) -> Result<()> {
|
|
||||||
self
|
|
||||||
.model
|
|
||||||
.set_show_grid_lines(sheet, show_grid_lines)
|
|
||||||
.map_err(to_js_error)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "getShowGridLines")]
|
|
||||||
pub fn get_show_grid_lines(&mut self, sheet: u32) -> Result<bool> {
|
|
||||||
self.model.get_show_grid_lines(sheet).map_err(to_js_error)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "autoFillRows")]
|
|
||||||
pub fn auto_fill_rows(&mut self, env: Env, source_area: JsUnknown, to_row: i32) -> Result<()> {
|
|
||||||
let area: Area = env
|
|
||||||
.from_js_value(source_area)
|
|
||||||
.map_err(|e| to_js_error(e.to_string()))?;
|
|
||||||
self
|
|
||||||
.model
|
|
||||||
.auto_fill_rows(&area, to_row)
|
|
||||||
.map_err(to_js_error)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "autoFillColumns")]
|
|
||||||
pub fn auto_fill_columns(
|
|
||||||
&mut self,
|
|
||||||
env: Env,
|
|
||||||
source_area: JsUnknown,
|
|
||||||
to_column: i32,
|
|
||||||
) -> Result<()> {
|
|
||||||
let area: Area = env
|
|
||||||
.from_js_value(source_area)
|
|
||||||
.map_err(|e| to_js_error(e.to_string()))?;
|
|
||||||
self
|
|
||||||
.model
|
|
||||||
.auto_fill_columns(&area, to_column)
|
|
||||||
.map_err(to_js_error)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "onArrowRight")]
|
|
||||||
pub fn on_arrow_right(&mut self) -> Result<()> {
|
|
||||||
self.model.on_arrow_right().map_err(to_js_error)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "onArrowLeft")]
|
|
||||||
pub fn on_arrow_left(&mut self) -> Result<()> {
|
|
||||||
self.model.on_arrow_left().map_err(to_js_error)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "onArrowUp")]
|
|
||||||
pub fn on_arrow_up(&mut self) -> Result<()> {
|
|
||||||
self.model.on_arrow_up().map_err(to_js_error)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "onArrowDown")]
|
|
||||||
pub fn on_arrow_down(&mut self) -> Result<()> {
|
|
||||||
self.model.on_arrow_down().map_err(to_js_error)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "onPageDown")]
|
|
||||||
pub fn on_page_down(&mut self) -> Result<()> {
|
|
||||||
self.model.on_page_down().map_err(to_js_error)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "onPageUp")]
|
|
||||||
pub fn on_page_up(&mut self) -> Result<()> {
|
|
||||||
self.model.on_page_up().map_err(to_js_error)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "setWindowWidth")]
|
|
||||||
pub fn set_window_width(&mut self, window_width: f64) {
|
|
||||||
self.model.set_window_width(window_width);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "setWindowHeight")]
|
|
||||||
pub fn set_window_height(&mut self, window_height: f64) {
|
|
||||||
self.model.set_window_height(window_height);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "getScrollX")]
|
|
||||||
pub fn get_scroll_x(&self) -> Result<f64> {
|
|
||||||
self.model.get_scroll_x().map_err(to_js_error)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "getScrollY")]
|
|
||||||
pub fn get_scroll_y(&self) -> Result<f64> {
|
|
||||||
self.model.get_scroll_y().map_err(to_js_error)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "onExpandSelectedRange")]
|
|
||||||
pub fn on_expand_selected_range(&mut self, key: String) -> Result<()> {
|
|
||||||
self
|
|
||||||
.model
|
|
||||||
.on_expand_selected_range(&key)
|
|
||||||
.map_err(to_js_error)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "onAreaSelecting")]
|
|
||||||
pub fn on_area_selecting(&mut self, target_row: i32, target_column: i32) -> Result<()> {
|
|
||||||
self
|
|
||||||
.model
|
|
||||||
.on_area_selecting(target_row, target_column)
|
|
||||||
.map_err(to_js_error)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "setAreaWithBorder")]
|
|
||||||
pub fn set_area_with_border(
|
|
||||||
&mut self,
|
|
||||||
env: Env,
|
|
||||||
area: JsUnknown,
|
|
||||||
border_area: JsUnknown,
|
|
||||||
) -> Result<()> {
|
|
||||||
let range: Area = env
|
|
||||||
.from_js_value(area)
|
|
||||||
.map_err(|e| to_js_error(e.to_string()))?;
|
|
||||||
let border: BorderArea = env
|
|
||||||
.from_js_value(border_area)
|
|
||||||
.map_err(|e| to_js_error(e.to_string()))?;
|
|
||||||
self
|
|
||||||
.model
|
|
||||||
.set_area_with_border(&range, &border)
|
|
||||||
.map_err(|e| to_js_error(e.to_string()))?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "toBytes")]
|
|
||||||
pub fn to_bytes(&self) -> Vec<u8> {
|
|
||||||
self.model.to_bytes()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "getName")]
|
|
||||||
pub fn get_name(&self) -> String {
|
|
||||||
self.model.get_name()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "setName")]
|
|
||||||
pub fn set_name(&mut self, name: String) {
|
|
||||||
self.model.set_name(&name);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "copyToClipboard")]
|
|
||||||
pub fn copy_to_clipboard(&self, env: Env) -> Result<JsUnknown> {
|
|
||||||
let data = self
|
|
||||||
.model
|
|
||||||
.copy_to_clipboard()
|
|
||||||
.map_err(|e| to_js_error(e.to_string()))?;
|
|
||||||
|
|
||||||
env
|
|
||||||
.to_js_value(&data)
|
|
||||||
.map_err(|e| to_js_error(e.to_string()))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "pasteFromClipboard")]
|
|
||||||
pub fn paste_from_clipboard(
|
|
||||||
&mut self,
|
|
||||||
env: Env,
|
|
||||||
source_sheet: u32,
|
|
||||||
source_range: JsUnknown,
|
|
||||||
clipboard: JsUnknown,
|
|
||||||
is_cut: bool,
|
|
||||||
) -> Result<()> {
|
|
||||||
let source_range: (i32, i32, i32, i32) = env
|
|
||||||
.from_js_value(source_range)
|
|
||||||
.map_err(|e| to_js_error(e.to_string()))?;
|
|
||||||
let clipboard: ClipboardData = env
|
|
||||||
.from_js_value(clipboard)
|
|
||||||
.map_err(|e| to_js_error(e.to_string()))?;
|
|
||||||
self
|
|
||||||
.model
|
|
||||||
.paste_from_clipboard(source_sheet, source_range, &clipboard, is_cut)
|
|
||||||
.map_err(|e| to_js_error(e.to_string()))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "pasteCsvText")]
|
|
||||||
pub fn paste_csv_string(&mut self, env: Env, area: JsUnknown, csv: String) -> Result<()> {
|
|
||||||
let range: Area = env
|
|
||||||
.from_js_value(area)
|
|
||||||
.map_err(|e| to_js_error(e.to_string()))?;
|
|
||||||
self
|
|
||||||
.model
|
|
||||||
.paste_csv_string(&range, &csv)
|
|
||||||
.map_err(|e| to_js_error(e.to_string()))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "getDefinedNameList")]
|
|
||||||
pub fn get_defined_name_list(&self, env: Env) -> Result<JsUnknown> {
|
|
||||||
let data: Vec<DefinedName> = self
|
|
||||||
.model
|
|
||||||
.get_defined_name_list()
|
|
||||||
.iter()
|
|
||||||
.map(|s| DefinedName {
|
|
||||||
name: s.0.to_owned(),
|
|
||||||
scope: s.1,
|
|
||||||
formula: s.2.to_owned(),
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
env
|
|
||||||
.to_js_value(&data)
|
|
||||||
.map_err(|e| to_js_error(e.to_string()))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "newDefinedName")]
|
|
||||||
pub fn new_defined_name(
|
|
||||||
&mut self,
|
|
||||||
name: String,
|
|
||||||
scope: Option<u32>,
|
|
||||||
formula: String,
|
|
||||||
) -> Result<()> {
|
|
||||||
self
|
|
||||||
.model
|
|
||||||
.new_defined_name(&name, scope, &formula)
|
|
||||||
.map_err(|e| to_js_error(e.to_string()))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "updateDefinedName")]
|
|
||||||
pub fn update_defined_name(
|
|
||||||
&mut self,
|
|
||||||
name: String,
|
|
||||||
scope: Option<u32>,
|
|
||||||
new_name: String,
|
|
||||||
new_scope: Option<u32>,
|
|
||||||
new_formula: String,
|
|
||||||
) -> Result<()> {
|
|
||||||
self
|
|
||||||
.model
|
|
||||||
.update_defined_name(&name, scope, &new_name, new_scope, &new_formula)
|
|
||||||
.map_err(|e| to_js_error(e.to_string()))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[napi(js_name = "deleteDefinedName")]
|
|
||||||
pub fn delete_definedname(&mut self, name: String, scope: Option<u32>) -> Result<()> {
|
|
||||||
self
|
|
||||||
.model
|
|
||||||
.delete_defined_name(&name, scope)
|
|
||||||
.map_err(|e| to_js_error(e.to_string()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
343
bindings/nodejs/src/model.rs
Normal file
343
bindings/nodejs/src/model.rs
Normal file
@@ -0,0 +1,343 @@
|
|||||||
|
#![deny(clippy::all)]
|
||||||
|
|
||||||
|
use napi::{self, bindgen_prelude::*, JsUnknown, Result};
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use ironcalc::{
|
||||||
|
base::{
|
||||||
|
types::{CellType, Style},
|
||||||
|
Model as BaseModel,
|
||||||
|
},
|
||||||
|
error::XlsxError,
|
||||||
|
export::{save_to_icalc, save_to_xlsx},
|
||||||
|
import::{load_from_icalc, load_from_xlsx},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct DefinedName {
|
||||||
|
name: String,
|
||||||
|
scope: Option<u32>,
|
||||||
|
formula: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_js_error(error: String) -> Error {
|
||||||
|
Error::new(Status::Unknown, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_node_error(error: XlsxError) -> Error {
|
||||||
|
Error::new(Status::Unknown, error.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub struct Model {
|
||||||
|
model: BaseModel,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
impl Model {
|
||||||
|
#[napi(constructor)]
|
||||||
|
pub fn new(name: String, locale: String, timezone: String) -> Result<Self> {
|
||||||
|
let model = BaseModel::new_empty(&name, &locale, &timezone).map_err(to_js_error)?;
|
||||||
|
Ok(Self { model })
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(factory)]
|
||||||
|
pub fn from_xlsx(file_path: String, locale: String, tz: String) -> Result<Model> {
|
||||||
|
let model = load_from_xlsx(&file_path, &locale, &tz)
|
||||||
|
.map_err(|error| Error::new(Status::Unknown, error.to_string()))?;
|
||||||
|
Ok(Self { model })
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(factory)]
|
||||||
|
pub fn from_icalc(file_name: String) -> Result<Model> {
|
||||||
|
let model = load_from_icalc(&file_name)
|
||||||
|
.map_err(|error| Error::new(Status::Unknown, error.to_string()))?;
|
||||||
|
Ok(Self { model })
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn save_to_xlsx(&self, file: String) -> Result<()> {
|
||||||
|
save_to_xlsx(&self.model, &file).map_err(to_node_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn save_to_icalc(&self, file: String) -> Result<()> {
|
||||||
|
save_to_icalc(&self.model, &file).map_err(to_node_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn evaluate(&mut self) {
|
||||||
|
self.model.evaluate();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn set_user_input(&mut self, sheet: u32, row: i32, column: i32, value: String) -> Result<()> {
|
||||||
|
self
|
||||||
|
.model
|
||||||
|
.set_user_input(sheet, row, column, value)
|
||||||
|
.map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn clear_cell_contents(&mut self, sheet: u32, row: i32, column: i32) -> Result<()> {
|
||||||
|
self
|
||||||
|
.model
|
||||||
|
.cell_clear_contents(sheet, row, column)
|
||||||
|
.map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn get_cell_content(&self, sheet: u32, row: i32, column: i32) -> Result<String> {
|
||||||
|
self
|
||||||
|
.model
|
||||||
|
.get_cell_content(sheet, row, column)
|
||||||
|
.map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "getCellType")]
|
||||||
|
pub fn get_cell_type(&self, sheet: u32, row: i32, column: i32) -> Result<i32> {
|
||||||
|
Ok(
|
||||||
|
match self
|
||||||
|
.model
|
||||||
|
.get_cell_type(sheet, row, column)
|
||||||
|
.map_err(to_js_error)?
|
||||||
|
{
|
||||||
|
CellType::Number => 1,
|
||||||
|
CellType::Text => 2,
|
||||||
|
CellType::LogicalValue => 4,
|
||||||
|
CellType::ErrorValue => 16,
|
||||||
|
CellType::Array => 64,
|
||||||
|
CellType::CompoundData => 128,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn get_formatted_cell_value(&self, sheet: u32, row: i32, column: i32) -> Result<String> {
|
||||||
|
self
|
||||||
|
.model
|
||||||
|
.get_formatted_cell_value(sheet, row, column)
|
||||||
|
.map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn set_cell_style(
|
||||||
|
&mut self,
|
||||||
|
env: Env,
|
||||||
|
sheet: u32,
|
||||||
|
row: i32,
|
||||||
|
column: i32,
|
||||||
|
style: JsUnknown,
|
||||||
|
) -> Result<()> {
|
||||||
|
let style: Style = env
|
||||||
|
.from_js_value(style)
|
||||||
|
.map_err(|e| to_js_error(e.to_string()))?;
|
||||||
|
self
|
||||||
|
.model
|
||||||
|
.set_cell_style(sheet, row, column, &style)
|
||||||
|
.map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "getCellStyle")]
|
||||||
|
pub fn get_cell_style(
|
||||||
|
&mut self,
|
||||||
|
env: Env,
|
||||||
|
sheet: u32,
|
||||||
|
row: i32,
|
||||||
|
column: i32,
|
||||||
|
) -> Result<JsUnknown> {
|
||||||
|
let style = self
|
||||||
|
.model
|
||||||
|
.get_style_for_cell(sheet, row, column)
|
||||||
|
.map_err(to_js_error)?;
|
||||||
|
|
||||||
|
env
|
||||||
|
.to_js_value(&style)
|
||||||
|
.map_err(|e| to_js_error(e.to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn insert_rows(&mut self, sheet: u32, row: i32, row_count: i32) -> Result<()> {
|
||||||
|
self
|
||||||
|
.model
|
||||||
|
.insert_rows(sheet, row, row_count)
|
||||||
|
.map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn insert_columns(&mut self, sheet: u32, column: i32, column_count: i32) -> Result<()> {
|
||||||
|
self
|
||||||
|
.model
|
||||||
|
.insert_columns(sheet, column, column_count)
|
||||||
|
.map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn delete_rows(&mut self, sheet: u32, row: i32, row_count: i32) -> Result<()> {
|
||||||
|
self
|
||||||
|
.model
|
||||||
|
.delete_rows(sheet, row, row_count)
|
||||||
|
.map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn delete_columns(&mut self, sheet: u32, column: i32, column_count: i32) -> Result<()> {
|
||||||
|
self
|
||||||
|
.model
|
||||||
|
.delete_columns(sheet, column, column_count)
|
||||||
|
.map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn get_column_width(&self, sheet: u32, column: i32) -> Result<f64> {
|
||||||
|
self
|
||||||
|
.model
|
||||||
|
.get_column_width(sheet, column)
|
||||||
|
.map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn get_row_height(&self, sheet: u32, row: i32) -> Result<f64> {
|
||||||
|
self.model.get_row_height(sheet, row).map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn set_column_width(&mut self, sheet: u32, column: i32, width: f64) -> Result<()> {
|
||||||
|
self
|
||||||
|
.model
|
||||||
|
.set_column_width(sheet, column, width)
|
||||||
|
.map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn set_row_height(&mut self, sheet: u32, row: i32, height: f64) -> Result<()> {
|
||||||
|
self
|
||||||
|
.model
|
||||||
|
.set_row_height(sheet, row, height)
|
||||||
|
.map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn get_frozen_columns_count(&self, sheet: u32) -> Result<i32> {
|
||||||
|
self
|
||||||
|
.model
|
||||||
|
.get_frozen_columns_count(sheet)
|
||||||
|
.map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn get_frozen_rows_count(&self, sheet: u32) -> Result<i32> {
|
||||||
|
self.model.get_frozen_rows_count(sheet).map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn set_frozen_columns_count(&mut self, sheet: u32, column_count: i32) -> Result<()> {
|
||||||
|
self
|
||||||
|
.model
|
||||||
|
.set_frozen_columns(sheet, column_count)
|
||||||
|
.map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn set_frozen_rows_count(&mut self, sheet: u32, row_count: i32) -> Result<()> {
|
||||||
|
self
|
||||||
|
.model
|
||||||
|
.set_frozen_rows(sheet, row_count)
|
||||||
|
.map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// I don't _think_ serializing to JsUnknown can't fail
|
||||||
|
// FIXME: Remove this clippy directive
|
||||||
|
#[napi(js_name = "getWorksheetsProperties")]
|
||||||
|
#[allow(clippy::unwrap_used)]
|
||||||
|
pub fn get_worksheets_properties(&self, env: Env) -> JsUnknown {
|
||||||
|
env
|
||||||
|
.to_js_value(&self.model.get_worksheets_properties())
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn set_sheet_color(&mut self, sheet: u32, color: String) -> Result<()> {
|
||||||
|
self
|
||||||
|
.model
|
||||||
|
.set_sheet_color(sheet, &color)
|
||||||
|
.map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn add_sheet(&mut self, sheet_name: String) -> Result<()> {
|
||||||
|
self.model.add_sheet(&sheet_name).map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn new_sheet(&mut self) {
|
||||||
|
self.model.new_sheet();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn delete_sheet(&mut self, sheet: u32) -> Result<()> {
|
||||||
|
self.model.delete_sheet(sheet).map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn rename_sheet(&mut self, sheet: u32, new_name: String) -> Result<()> {
|
||||||
|
self
|
||||||
|
.model
|
||||||
|
.rename_sheet_by_index(sheet, &new_name)
|
||||||
|
.map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "getDefinedNameList")]
|
||||||
|
pub fn get_defined_name_list(&self, env: Env) -> Result<JsUnknown> {
|
||||||
|
let data: Vec<DefinedName> = self
|
||||||
|
.model
|
||||||
|
.workbook
|
||||||
|
.get_defined_names_with_scope()
|
||||||
|
.iter()
|
||||||
|
.map(|s| DefinedName {
|
||||||
|
name: s.0.to_owned(),
|
||||||
|
scope: s.1,
|
||||||
|
formula: s.2.to_owned(),
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
env
|
||||||
|
.to_js_value(&data)
|
||||||
|
.map_err(|e| to_js_error(e.to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "newDefinedName")]
|
||||||
|
pub fn new_defined_name(
|
||||||
|
&mut self,
|
||||||
|
name: String,
|
||||||
|
scope: Option<u32>,
|
||||||
|
formula: String,
|
||||||
|
) -> Result<()> {
|
||||||
|
self
|
||||||
|
.model
|
||||||
|
.new_defined_name(&name, scope, &formula)
|
||||||
|
.map_err(|e| to_js_error(e.to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "updateDefinedName")]
|
||||||
|
pub fn update_defined_name(
|
||||||
|
&mut self,
|
||||||
|
name: String,
|
||||||
|
scope: Option<u32>,
|
||||||
|
new_name: String,
|
||||||
|
new_scope: Option<u32>,
|
||||||
|
new_formula: String,
|
||||||
|
) -> Result<()> {
|
||||||
|
self
|
||||||
|
.model
|
||||||
|
.update_defined_name(&name, scope, &new_name, new_scope, &new_formula)
|
||||||
|
.map_err(|e| to_js_error(e.to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "deleteDefinedName")]
|
||||||
|
pub fn delete_definedname(&mut self, name: String, scope: Option<u32>) -> Result<()> {
|
||||||
|
self
|
||||||
|
.model
|
||||||
|
.delete_defined_name(&name, scope)
|
||||||
|
.map_err(|e| to_js_error(e.to_string()))
|
||||||
|
}
|
||||||
|
}
|
||||||
620
bindings/nodejs/src/user_model.rs
Normal file
620
bindings/nodejs/src/user_model.rs
Normal file
@@ -0,0 +1,620 @@
|
|||||||
|
#![deny(clippy::all)]
|
||||||
|
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use napi::{self, bindgen_prelude::*, JsUnknown, Result};
|
||||||
|
|
||||||
|
use ironcalc::base::{
|
||||||
|
expressions::types::Area,
|
||||||
|
types::{CellType, Style},
|
||||||
|
BorderArea, ClipboardData, UserModel as BaseModel,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct DefinedName {
|
||||||
|
name: String,
|
||||||
|
scope: Option<u32>,
|
||||||
|
formula: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_js_error(error: String) -> Error {
|
||||||
|
Error::new(Status::Unknown, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub struct UserModel {
|
||||||
|
model: BaseModel,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
impl UserModel {
|
||||||
|
#[napi(constructor)]
|
||||||
|
pub fn new(name: String, locale: String, timezone: String) -> Result<Self> {
|
||||||
|
let model = BaseModel::new_empty(&name, &locale, &timezone).map_err(to_js_error)?;
|
||||||
|
Ok(Self { model })
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(factory)]
|
||||||
|
pub fn from_bytes(bytes: &[u8]) -> Result<UserModel> {
|
||||||
|
let model = BaseModel::from_bytes(bytes).map_err(to_js_error)?;
|
||||||
|
Ok(UserModel { model })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn undo(&mut self) -> Result<()> {
|
||||||
|
self.model.undo().map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn redo(&mut self) -> Result<()> {
|
||||||
|
self.model.redo().map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "canUndo")]
|
||||||
|
pub fn can_undo(&self) -> bool {
|
||||||
|
self.model.can_undo()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "canRedo")]
|
||||||
|
pub fn can_redo(&self) -> bool {
|
||||||
|
self.model.can_redo()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "pauseEvaluation")]
|
||||||
|
pub fn pause_evaluation(&mut self) {
|
||||||
|
self.model.pause_evaluation()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "resumeEvaluation")]
|
||||||
|
pub fn resume_evaluation(&mut self) {
|
||||||
|
self.model.resume_evaluation()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn evaluate(&mut self) {
|
||||||
|
self.model.evaluate();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "flushSendQueue")]
|
||||||
|
pub fn flush_send_queue(&mut self) -> Vec<u8> {
|
||||||
|
self.model.flush_send_queue()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "applyExternalDiffs")]
|
||||||
|
pub fn apply_external_diffs(&mut self, diffs: &[u8]) -> Result<()> {
|
||||||
|
self.model.apply_external_diffs(diffs).map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "getCellContent")]
|
||||||
|
pub fn get_cell_content(&self, sheet: u32, row: i32, column: i32) -> Result<String> {
|
||||||
|
self
|
||||||
|
.model
|
||||||
|
.get_cell_content(sheet, row, column)
|
||||||
|
.map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "newSheet")]
|
||||||
|
pub fn new_sheet(&mut self) -> Result<()> {
|
||||||
|
self.model.new_sheet().map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "deleteSheet")]
|
||||||
|
pub fn delete_sheet(&mut self, sheet: u32) -> Result<()> {
|
||||||
|
self.model.delete_sheet(sheet).map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "hideSheet")]
|
||||||
|
pub fn hide_sheet(&mut self, sheet: u32) -> Result<()> {
|
||||||
|
self.model.hide_sheet(sheet).map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "unhideSheet")]
|
||||||
|
pub fn unhide_sheet(&mut self, sheet: u32) -> Result<()> {
|
||||||
|
self.model.unhide_sheet(sheet).map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "renameSheet")]
|
||||||
|
pub fn rename_sheet(&mut self, sheet: u32, name: String) -> Result<()> {
|
||||||
|
self.model.rename_sheet(sheet, &name).map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "setSheetColor")]
|
||||||
|
pub fn set_sheet_color(&mut self, sheet: u32, color: String) -> Result<()> {
|
||||||
|
self
|
||||||
|
.model
|
||||||
|
.set_sheet_color(sheet, &color)
|
||||||
|
.map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "rangeClearAll")]
|
||||||
|
pub fn range_clear_all(
|
||||||
|
&mut self,
|
||||||
|
sheet: u32,
|
||||||
|
start_row: i32,
|
||||||
|
start_column: i32,
|
||||||
|
end_row: i32,
|
||||||
|
end_column: i32,
|
||||||
|
) -> Result<()> {
|
||||||
|
let range = Area {
|
||||||
|
sheet,
|
||||||
|
row: start_row,
|
||||||
|
column: start_column,
|
||||||
|
width: end_column - start_column + 1,
|
||||||
|
height: end_row - start_row + 1,
|
||||||
|
};
|
||||||
|
self.model.range_clear_all(&range).map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "rangeClearContents")]
|
||||||
|
pub fn range_clear_contents(
|
||||||
|
&mut self,
|
||||||
|
sheet: u32,
|
||||||
|
start_row: i32,
|
||||||
|
start_column: i32,
|
||||||
|
end_row: i32,
|
||||||
|
end_column: i32,
|
||||||
|
) -> Result<()> {
|
||||||
|
let range = Area {
|
||||||
|
sheet,
|
||||||
|
row: start_row,
|
||||||
|
column: start_column,
|
||||||
|
width: end_column - start_column + 1,
|
||||||
|
height: end_row - start_row + 1,
|
||||||
|
};
|
||||||
|
self.model.range_clear_contents(&range).map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "insertRow")]
|
||||||
|
pub fn insert_row(&mut self, sheet: u32, row: i32) -> Result<()> {
|
||||||
|
self.model.insert_row(sheet, row).map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "insertColumn")]
|
||||||
|
pub fn insert_column(&mut self, sheet: u32, column: i32) -> Result<()> {
|
||||||
|
self.model.insert_column(sheet, column).map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "deleteRow")]
|
||||||
|
pub fn delete_row(&mut self, sheet: u32, row: i32) -> Result<()> {
|
||||||
|
self.model.delete_row(sheet, row).map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "deleteColumn")]
|
||||||
|
pub fn delete_column(&mut self, sheet: u32, column: i32) -> Result<()> {
|
||||||
|
self.model.delete_column(sheet, column).map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "setRowHeight")]
|
||||||
|
pub fn set_row_height(&mut self, sheet: u32, row: i32, height: f64) -> Result<()> {
|
||||||
|
self
|
||||||
|
.model
|
||||||
|
.set_row_height(sheet, row, height)
|
||||||
|
.map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "setColumnWidth")]
|
||||||
|
pub fn set_column_width(&mut self, sheet: u32, column: i32, width: f64) -> Result<()> {
|
||||||
|
self
|
||||||
|
.model
|
||||||
|
.set_column_width(sheet, column, width)
|
||||||
|
.map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "getRowHeight")]
|
||||||
|
pub fn get_row_height(&mut self, sheet: u32, row: i32) -> Result<f64> {
|
||||||
|
self.model.get_row_height(sheet, row).map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "getColumnWidth")]
|
||||||
|
pub fn get_column_width(&mut self, sheet: u32, column: i32) -> Result<f64> {
|
||||||
|
self
|
||||||
|
.model
|
||||||
|
.get_column_width(sheet, column)
|
||||||
|
.map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "setUserInput")]
|
||||||
|
pub fn set_user_input(&mut self, sheet: u32, row: i32, column: i32, input: String) -> Result<()> {
|
||||||
|
self
|
||||||
|
.model
|
||||||
|
.set_user_input(sheet, row, column, &input)
|
||||||
|
.map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "getFormattedCellValue")]
|
||||||
|
pub fn get_formatted_cell_value(&self, sheet: u32, row: i32, column: i32) -> Result<String> {
|
||||||
|
self
|
||||||
|
.model
|
||||||
|
.get_formatted_cell_value(sheet, row, column)
|
||||||
|
.map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "getFrozenRowsCount")]
|
||||||
|
pub fn get_frozen_rows_count(&self, sheet: u32) -> Result<i32> {
|
||||||
|
self.model.get_frozen_rows_count(sheet).map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "getFrozenColumnsCount")]
|
||||||
|
pub fn get_frozen_columns_count(&self, sheet: u32) -> Result<i32> {
|
||||||
|
self
|
||||||
|
.model
|
||||||
|
.get_frozen_columns_count(sheet)
|
||||||
|
.map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "setFrozenRowsCount")]
|
||||||
|
pub fn set_frozen_rows_count(&mut self, sheet: u32, count: i32) -> Result<()> {
|
||||||
|
self
|
||||||
|
.model
|
||||||
|
.set_frozen_rows_count(sheet, count)
|
||||||
|
.map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "setFrozenColumnsCount")]
|
||||||
|
pub fn set_frozen_columns_count(&mut self, sheet: u32, count: i32) -> Result<()> {
|
||||||
|
self
|
||||||
|
.model
|
||||||
|
.set_frozen_columns_count(sheet, count)
|
||||||
|
.map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "updateRangeStyle")]
|
||||||
|
pub fn update_range_style(
|
||||||
|
&mut self,
|
||||||
|
env: Env,
|
||||||
|
range: JsUnknown,
|
||||||
|
style_path: String,
|
||||||
|
value: String,
|
||||||
|
) -> Result<()> {
|
||||||
|
let range: Area = env
|
||||||
|
.from_js_value(range)
|
||||||
|
.map_err(|e| to_js_error(e.to_string()))?;
|
||||||
|
self
|
||||||
|
.model
|
||||||
|
.update_range_style(&range, &style_path, &value)
|
||||||
|
.map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "getCellStyle")]
|
||||||
|
pub fn get_cell_style(
|
||||||
|
&mut self,
|
||||||
|
env: Env,
|
||||||
|
sheet: u32,
|
||||||
|
row: i32,
|
||||||
|
column: i32,
|
||||||
|
) -> Result<JsUnknown> {
|
||||||
|
let style = self
|
||||||
|
.model
|
||||||
|
.get_cell_style(sheet, row, column)
|
||||||
|
.map_err(to_js_error)?;
|
||||||
|
|
||||||
|
env
|
||||||
|
.to_js_value(&style)
|
||||||
|
.map_err(|e| to_js_error(e.to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "onPasteStyles")]
|
||||||
|
pub fn on_paste_styles(&mut self, env: Env, styles: JsUnknown) -> Result<()> {
|
||||||
|
let styles: &Vec<Vec<Style>> = &env
|
||||||
|
.from_js_value(styles)
|
||||||
|
.map_err(|e| to_js_error(e.to_string()))?;
|
||||||
|
self.model.on_paste_styles(styles).map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "getCellType")]
|
||||||
|
pub fn get_cell_type(&self, sheet: u32, row: i32, column: i32) -> Result<i32> {
|
||||||
|
Ok(
|
||||||
|
match self
|
||||||
|
.model
|
||||||
|
.get_cell_type(sheet, row, column)
|
||||||
|
.map_err(to_js_error)?
|
||||||
|
{
|
||||||
|
CellType::Number => 1,
|
||||||
|
CellType::Text => 2,
|
||||||
|
CellType::LogicalValue => 4,
|
||||||
|
CellType::ErrorValue => 16,
|
||||||
|
CellType::Array => 64,
|
||||||
|
CellType::CompoundData => 128,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// I don't _think_ serializing to JsUnknown can't fail
|
||||||
|
// FIXME: Remove this clippy directive
|
||||||
|
#[napi(js_name = "getWorksheetsProperties")]
|
||||||
|
#[allow(clippy::unwrap_used)]
|
||||||
|
pub fn get_worksheets_properties(&self, env: Env) -> JsUnknown {
|
||||||
|
env
|
||||||
|
.to_js_value(&self.model.get_worksheets_properties())
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "getSelectedSheet")]
|
||||||
|
pub fn get_selected_sheet(&self) -> u32 {
|
||||||
|
self.model.get_selected_sheet()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "getSelectedCell")]
|
||||||
|
pub fn get_selected_cell(&self) -> Vec<i32> {
|
||||||
|
let (sheet, row, column) = self.model.get_selected_cell();
|
||||||
|
vec![sheet as i32, row, column]
|
||||||
|
}
|
||||||
|
|
||||||
|
// I don't _think_ serializing to JsUnknown can't fail
|
||||||
|
// FIXME: Remove this clippy directive
|
||||||
|
#[napi(js_name = "getSelectedView")]
|
||||||
|
#[allow(clippy::unwrap_used)]
|
||||||
|
pub fn get_selected_view(&self, env: Env) -> JsUnknown {
|
||||||
|
env.to_js_value(&self.model.get_selected_view()).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "setSelectedSheet")]
|
||||||
|
pub fn set_selected_sheet(&mut self, sheet: u32) -> Result<()> {
|
||||||
|
self.model.set_selected_sheet(sheet).map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "setSelectedCell")]
|
||||||
|
pub fn set_selected_cell(&mut self, row: i32, column: i32) -> Result<()> {
|
||||||
|
self
|
||||||
|
.model
|
||||||
|
.set_selected_cell(row, column)
|
||||||
|
.map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "setSelectedRange")]
|
||||||
|
pub fn set_selected_range(
|
||||||
|
&mut self,
|
||||||
|
start_row: i32,
|
||||||
|
start_column: i32,
|
||||||
|
end_row: i32,
|
||||||
|
end_column: i32,
|
||||||
|
) -> Result<()> {
|
||||||
|
self
|
||||||
|
.model
|
||||||
|
.set_selected_range(start_row, start_column, end_row, end_column)
|
||||||
|
.map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "setTopLeftVisibleCell")]
|
||||||
|
pub fn set_top_left_visible_cell(&mut self, top_row: i32, top_column: i32) -> Result<()> {
|
||||||
|
self
|
||||||
|
.model
|
||||||
|
.set_top_left_visible_cell(top_row, top_column)
|
||||||
|
.map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "setShowGridLines")]
|
||||||
|
pub fn set_show_grid_lines(&mut self, sheet: u32, show_grid_lines: bool) -> Result<()> {
|
||||||
|
self
|
||||||
|
.model
|
||||||
|
.set_show_grid_lines(sheet, show_grid_lines)
|
||||||
|
.map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "getShowGridLines")]
|
||||||
|
pub fn get_show_grid_lines(&mut self, sheet: u32) -> Result<bool> {
|
||||||
|
self.model.get_show_grid_lines(sheet).map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "autoFillRows")]
|
||||||
|
pub fn auto_fill_rows(&mut self, env: Env, source_area: JsUnknown, to_row: i32) -> Result<()> {
|
||||||
|
let area: Area = env
|
||||||
|
.from_js_value(source_area)
|
||||||
|
.map_err(|e| to_js_error(e.to_string()))?;
|
||||||
|
self
|
||||||
|
.model
|
||||||
|
.auto_fill_rows(&area, to_row)
|
||||||
|
.map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "autoFillColumns")]
|
||||||
|
pub fn auto_fill_columns(
|
||||||
|
&mut self,
|
||||||
|
env: Env,
|
||||||
|
source_area: JsUnknown,
|
||||||
|
to_column: i32,
|
||||||
|
) -> Result<()> {
|
||||||
|
let area: Area = env
|
||||||
|
.from_js_value(source_area)
|
||||||
|
.map_err(|e| to_js_error(e.to_string()))?;
|
||||||
|
self
|
||||||
|
.model
|
||||||
|
.auto_fill_columns(&area, to_column)
|
||||||
|
.map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "onArrowRight")]
|
||||||
|
pub fn on_arrow_right(&mut self) -> Result<()> {
|
||||||
|
self.model.on_arrow_right().map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "onArrowLeft")]
|
||||||
|
pub fn on_arrow_left(&mut self) -> Result<()> {
|
||||||
|
self.model.on_arrow_left().map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "onArrowUp")]
|
||||||
|
pub fn on_arrow_up(&mut self) -> Result<()> {
|
||||||
|
self.model.on_arrow_up().map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "onArrowDown")]
|
||||||
|
pub fn on_arrow_down(&mut self) -> Result<()> {
|
||||||
|
self.model.on_arrow_down().map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "onPageDown")]
|
||||||
|
pub fn on_page_down(&mut self) -> Result<()> {
|
||||||
|
self.model.on_page_down().map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "onPageUp")]
|
||||||
|
pub fn on_page_up(&mut self) -> Result<()> {
|
||||||
|
self.model.on_page_up().map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "setWindowWidth")]
|
||||||
|
pub fn set_window_width(&mut self, window_width: f64) {
|
||||||
|
self.model.set_window_width(window_width);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "setWindowHeight")]
|
||||||
|
pub fn set_window_height(&mut self, window_height: f64) {
|
||||||
|
self.model.set_window_height(window_height);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "getScrollX")]
|
||||||
|
pub fn get_scroll_x(&self) -> Result<f64> {
|
||||||
|
self.model.get_scroll_x().map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "getScrollY")]
|
||||||
|
pub fn get_scroll_y(&self) -> Result<f64> {
|
||||||
|
self.model.get_scroll_y().map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "onExpandSelectedRange")]
|
||||||
|
pub fn on_expand_selected_range(&mut self, key: String) -> Result<()> {
|
||||||
|
self
|
||||||
|
.model
|
||||||
|
.on_expand_selected_range(&key)
|
||||||
|
.map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "onAreaSelecting")]
|
||||||
|
pub fn on_area_selecting(&mut self, target_row: i32, target_column: i32) -> Result<()> {
|
||||||
|
self
|
||||||
|
.model
|
||||||
|
.on_area_selecting(target_row, target_column)
|
||||||
|
.map_err(to_js_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "setAreaWithBorder")]
|
||||||
|
pub fn set_area_with_border(
|
||||||
|
&mut self,
|
||||||
|
env: Env,
|
||||||
|
area: JsUnknown,
|
||||||
|
border_area: JsUnknown,
|
||||||
|
) -> Result<()> {
|
||||||
|
let range: Area = env
|
||||||
|
.from_js_value(area)
|
||||||
|
.map_err(|e| to_js_error(e.to_string()))?;
|
||||||
|
let border: BorderArea = env
|
||||||
|
.from_js_value(border_area)
|
||||||
|
.map_err(|e| to_js_error(e.to_string()))?;
|
||||||
|
self
|
||||||
|
.model
|
||||||
|
.set_area_with_border(&range, &border)
|
||||||
|
.map_err(|e| to_js_error(e.to_string()))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "toBytes")]
|
||||||
|
pub fn to_bytes(&self) -> Vec<u8> {
|
||||||
|
self.model.to_bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "getName")]
|
||||||
|
pub fn get_name(&self) -> String {
|
||||||
|
self.model.get_name()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "setName")]
|
||||||
|
pub fn set_name(&mut self, name: String) {
|
||||||
|
self.model.set_name(&name);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "copyToClipboard")]
|
||||||
|
pub fn copy_to_clipboard(&self, env: Env) -> Result<JsUnknown> {
|
||||||
|
let data = self
|
||||||
|
.model
|
||||||
|
.copy_to_clipboard()
|
||||||
|
.map_err(|e| to_js_error(e.to_string()))?;
|
||||||
|
|
||||||
|
env
|
||||||
|
.to_js_value(&data)
|
||||||
|
.map_err(|e| to_js_error(e.to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "pasteFromClipboard")]
|
||||||
|
pub fn paste_from_clipboard(
|
||||||
|
&mut self,
|
||||||
|
env: Env,
|
||||||
|
source_sheet: u32,
|
||||||
|
source_range: JsUnknown,
|
||||||
|
clipboard: JsUnknown,
|
||||||
|
is_cut: bool,
|
||||||
|
) -> Result<()> {
|
||||||
|
let source_range: (i32, i32, i32, i32) = env
|
||||||
|
.from_js_value(source_range)
|
||||||
|
.map_err(|e| to_js_error(e.to_string()))?;
|
||||||
|
let clipboard: ClipboardData = env
|
||||||
|
.from_js_value(clipboard)
|
||||||
|
.map_err(|e| to_js_error(e.to_string()))?;
|
||||||
|
self
|
||||||
|
.model
|
||||||
|
.paste_from_clipboard(source_sheet, source_range, &clipboard, is_cut)
|
||||||
|
.map_err(|e| to_js_error(e.to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "pasteCsvText")]
|
||||||
|
pub fn paste_csv_string(&mut self, env: Env, area: JsUnknown, csv: String) -> Result<()> {
|
||||||
|
let range: Area = env
|
||||||
|
.from_js_value(area)
|
||||||
|
.map_err(|e| to_js_error(e.to_string()))?;
|
||||||
|
self
|
||||||
|
.model
|
||||||
|
.paste_csv_string(&range, &csv)
|
||||||
|
.map_err(|e| to_js_error(e.to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "getDefinedNameList")]
|
||||||
|
pub fn get_defined_name_list(&self, env: Env) -> Result<JsUnknown> {
|
||||||
|
let data: Vec<DefinedName> = self
|
||||||
|
.model
|
||||||
|
.get_defined_name_list()
|
||||||
|
.iter()
|
||||||
|
.map(|s| DefinedName {
|
||||||
|
name: s.0.to_owned(),
|
||||||
|
scope: s.1,
|
||||||
|
formula: s.2.to_owned(),
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
env
|
||||||
|
.to_js_value(&data)
|
||||||
|
.map_err(|e| to_js_error(e.to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "newDefinedName")]
|
||||||
|
pub fn new_defined_name(
|
||||||
|
&mut self,
|
||||||
|
name: String,
|
||||||
|
scope: Option<u32>,
|
||||||
|
formula: String,
|
||||||
|
) -> Result<()> {
|
||||||
|
self
|
||||||
|
.model
|
||||||
|
.new_defined_name(&name, scope, &formula)
|
||||||
|
.map_err(|e| to_js_error(e.to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "updateDefinedName")]
|
||||||
|
pub fn update_defined_name(
|
||||||
|
&mut self,
|
||||||
|
name: String,
|
||||||
|
scope: Option<u32>,
|
||||||
|
new_name: String,
|
||||||
|
new_scope: Option<u32>,
|
||||||
|
new_formula: String,
|
||||||
|
) -> Result<()> {
|
||||||
|
self
|
||||||
|
.model
|
||||||
|
.update_defined_name(&name, scope, &new_name, new_scope, &new_formula)
|
||||||
|
.map_err(|e| to_js_error(e.to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(js_name = "deleteDefinedName")]
|
||||||
|
pub fn delete_definedname(&mut self, name: String, scope: Option<u32>) -> Result<()> {
|
||||||
|
self
|
||||||
|
.model
|
||||||
|
.delete_defined_name(&name, scope)
|
||||||
|
.map_err(|e| to_js_error(e.to_string()))
|
||||||
|
}
|
||||||
|
}
|
||||||
142
webapp/IronCalc/src/components/SheetTabBar/SheetDeleteDialog.tsx
Normal file
142
webapp/IronCalc/src/components/SheetTabBar/SheetDeleteDialog.tsx
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
import styled from "@emotion/styled";
|
||||||
|
import { Button, Dialog } from "@mui/material";
|
||||||
|
import { Trash2 } from "lucide-react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { theme } from "../../theme";
|
||||||
|
|
||||||
|
interface SheetDeleteDialogProps {
|
||||||
|
open: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
onDelete: () => void;
|
||||||
|
sheetName: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function SheetDeleteDialog({
|
||||||
|
open,
|
||||||
|
onClose,
|
||||||
|
onDelete,
|
||||||
|
sheetName,
|
||||||
|
}: SheetDeleteDialogProps) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog open={open} onClose={onClose}>
|
||||||
|
<DialogWrapper>
|
||||||
|
<IconWrapper>
|
||||||
|
<Trash2 />
|
||||||
|
</IconWrapper>
|
||||||
|
<Title>{t("sheet_delete.title")}</Title>
|
||||||
|
<Body>
|
||||||
|
{t("sheet_delete.message", {
|
||||||
|
sheetName,
|
||||||
|
})}
|
||||||
|
</Body>
|
||||||
|
<ButtonGroup>
|
||||||
|
<DeleteButton onClick={onDelete} autoFocus>
|
||||||
|
{t("sheet_delete.confirm")}
|
||||||
|
</DeleteButton>
|
||||||
|
<CancelButton onClick={onClose}>
|
||||||
|
{t("sheet_delete.cancel")}
|
||||||
|
</CancelButton>
|
||||||
|
</ButtonGroup>
|
||||||
|
</DialogWrapper>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const DialogWrapper = styled.div`
|
||||||
|
position: fixed;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
background: white;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 12px;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0px 1px 3px 0px ${theme.palette.common.black}1A;
|
||||||
|
width: 280px;
|
||||||
|
max-width: calc(100% - 40px);
|
||||||
|
z-index: 50;
|
||||||
|
font-family: "Inter", sans-serif;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const IconWrapper = styled.div`
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: ${theme.palette.error.main}1A;
|
||||||
|
margin: 12px auto 8px auto;
|
||||||
|
color: ${theme.palette.error.main};
|
||||||
|
svg {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Title = styled.h2`
|
||||||
|
margin: 0;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: ${theme.palette.grey["900"]};
|
||||||
|
text-align: center;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Body = styled.p`
|
||||||
|
margin: 0;
|
||||||
|
text-align: center;
|
||||||
|
color: ${theme.palette.grey["900"]};
|
||||||
|
font-size: 12px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ButtonGroup = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
margin-top: 8px;
|
||||||
|
width: 100%;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledButton = styled.button`
|
||||||
|
cursor: pointer;
|
||||||
|
color: ${theme.palette.common.white};
|
||||||
|
background-color: ${theme.palette.primary.main};
|
||||||
|
padding: 0px 10px;
|
||||||
|
height: 36px;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: none;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 14px;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
transition: background-color 150ms;
|
||||||
|
text-transform: none;
|
||||||
|
&:hover {
|
||||||
|
background-color: ${theme.palette.primary.dark};
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const DeleteButton = styled(Button)`
|
||||||
|
background-color: ${theme.palette.error.main};
|
||||||
|
color: ${theme.palette.common.white};
|
||||||
|
text-transform: none;
|
||||||
|
&:hover {
|
||||||
|
background-color: ${theme.palette.error.dark};
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const CancelButton = styled(Button)`
|
||||||
|
background-color: ${theme.palette.grey["200"]};
|
||||||
|
color: ${theme.palette.grey["700"]};
|
||||||
|
text-transform: none;
|
||||||
|
&:hover {
|
||||||
|
background-color: ${theme.palette.grey["300"]};
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default SheetDeleteDialog;
|
||||||
@@ -6,6 +6,7 @@ import { theme } from "../../theme";
|
|||||||
import ColorPicker from "../colorPicker";
|
import ColorPicker from "../colorPicker";
|
||||||
import { isInReferenceMode } from "../editor/util";
|
import { isInReferenceMode } from "../editor/util";
|
||||||
import type { WorkbookState } from "../workbookState";
|
import type { WorkbookState } from "../workbookState";
|
||||||
|
import SheetDeleteDialog from "./SheetDeleteDialog";
|
||||||
import SheetRenameDialog from "./SheetRenameDialog";
|
import SheetRenameDialog from "./SheetRenameDialog";
|
||||||
|
|
||||||
interface SheetTabProps {
|
interface SheetTabProps {
|
||||||
@@ -37,9 +38,20 @@ function SheetTab(props: SheetTabProps) {
|
|||||||
const handleCloseRenameDialog = () => {
|
const handleCloseRenameDialog = () => {
|
||||||
setRenameDialogOpen(false);
|
setRenameDialogOpen(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleOpenRenameDialog = () => {
|
const handleOpenRenameDialog = () => {
|
||||||
setRenameDialogOpen(true);
|
setRenameDialogOpen(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
||||||
|
|
||||||
|
const handleOpenDeleteDialog = () => {
|
||||||
|
setDeleteDialogOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCloseDeleteDialog = () => {
|
||||||
|
setDeleteDialogOpen(false);
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<TabWrapper
|
<TabWrapper
|
||||||
@@ -97,7 +109,7 @@ function SheetTab(props: SheetTabProps) {
|
|||||||
<StyledMenuItem
|
<StyledMenuItem
|
||||||
disabled={!props.canDelete}
|
disabled={!props.canDelete}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
props.onDeleted();
|
handleOpenDeleteDialog();
|
||||||
handleClose();
|
handleClose();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -134,6 +146,15 @@ function SheetTab(props: SheetTabProps) {
|
|||||||
anchorEl={colorButton}
|
anchorEl={colorButton}
|
||||||
open={colorPickerOpen}
|
open={colorPickerOpen}
|
||||||
/>
|
/>
|
||||||
|
<SheetDeleteDialog
|
||||||
|
open={deleteDialogOpen}
|
||||||
|
onClose={handleCloseDeleteDialog}
|
||||||
|
onDelete={() => {
|
||||||
|
props.onDeleted();
|
||||||
|
handleCloseDeleteDialog();
|
||||||
|
}}
|
||||||
|
sheetName={name}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,6 +68,12 @@
|
|||||||
"title": "Rename Sheet",
|
"title": "Rename Sheet",
|
||||||
"close": "Close dialog"
|
"close": "Close dialog"
|
||||||
},
|
},
|
||||||
|
"sheet_delete": {
|
||||||
|
"title": "Are you sure?",
|
||||||
|
"message": "The sheet '{{sheetName}}' will be permanently deleted. This action cannot be undone.",
|
||||||
|
"confirm": "Yes, delete sheet",
|
||||||
|
"cancel": "Cancel"
|
||||||
|
},
|
||||||
"formula_input": {
|
"formula_input": {
|
||||||
"update": "Update",
|
"update": "Update",
|
||||||
"label": "Formula",
|
"label": "Formula",
|
||||||
|
|||||||
Reference in New Issue
Block a user