FIX: users :)

This commit is contained in:
Nicolás Hatcher
2025-06-02 18:32:43 +02:00
parent 7635cbe1d1
commit abaeb3284a
13 changed files with 122 additions and 16 deletions

2
Cargo.lock generated
View File

@@ -1069,7 +1069,7 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]] [[package]]
name = "wasm" name = "wasm"
version = "0.5.0" version = "0.5.3"
dependencies = [ dependencies = [
"ironcalc_base", "ironcalc_base",
"serde", "serde",

View File

@@ -405,6 +405,7 @@ impl Model {
}, },
tables: HashMap::new(), tables: HashMap::new(),
views, views,
users: Vec::new(),
}; };
let parsed_formulas = Vec::new(); let parsed_formulas = Vec::new();
let worksheets = &workbook.worksheets; let worksheets = &workbook.worksheets;

View File

@@ -39,6 +39,14 @@ pub struct WorkbookView {
pub window_height: i64, pub window_height: i64,
} }
#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
pub struct WebUser {
pub id: String,
pub sheet: u32,
pub row: i32,
pub column: i32,
}
/// An internal representation of an IronCalc Workbook /// An internal representation of an IronCalc Workbook
#[derive(Encode, Decode, Debug, PartialEq, Clone)] #[derive(Encode, Decode, Debug, PartialEq, Clone)]
pub struct Workbook { pub struct Workbook {
@@ -51,6 +59,7 @@ pub struct Workbook {
pub metadata: Metadata, pub metadata: Metadata,
pub tables: HashMap<String, Table>, pub tables: HashMap<String, Table>,
pub views: HashMap<u32, WorkbookView>, pub views: HashMap<u32, WorkbookView>,
pub users: Vec<WebUser>
} }
/// A defined name. The `sheet_id` is the sheet index in case the name is local /// A defined name. The `sheet_id` is the sheet index in case the name is local

View File

@@ -14,7 +14,7 @@ use crate::{
model::Model, model::Model,
types::{ types::{
Alignment, BorderItem, CellType, Col, HorizontalAlignment, SheetProperties, SheetState, Alignment, BorderItem, CellType, Col, HorizontalAlignment, SheetProperties, SheetState,
Style, VerticalAlignment, Style, VerticalAlignment, WebUser,
}, },
utils::is_valid_hex_color, utils::is_valid_hex_color,
}; };
@@ -293,6 +293,11 @@ impl UserModel {
self.model.workbook.name = name.to_string(); self.model.workbook.name = name.to_string();
} }
/// Set users
pub fn set_users(&mut self, users: &[WebUser]) {
self.model.workbook.users = users.to_vec();
}
/// Undoes last change if any, places the change in the redo list and evaluates the model if needed /// Undoes last change if any, places the change in the redo list and evaluates the model if needed
/// ///
/// See also: /// See also:

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "wasm" name = "wasm"
version = "0.5.0" version = "0.5.3"
authors = ["Nicolas Hatcher <nicolas@theuniverse.today>"] authors = ["Nicolas Hatcher <nicolas@theuniverse.today>"]
description = "IronCalc Web bindings" description = "IronCalc Web bindings"
license = "MIT/Apache-2.0" license = "MIT/Apache-2.0"

View File

@@ -201,6 +201,36 @@ defined_name_list_types = r"""
getDefinedNameList(): DefinedName[]; getDefinedNameList(): DefinedName[];
""" """
set_users = r"""
/**
* @param {any} users
*/
setUsers(users: any): void;
"""
set_users_types = r"""
/**
* @param {WebUser[]} users
*/
setUsers(users: WebUser[]): void;
"""
get_users = r"""
/**
* @returns {any}
*/
getUsers(): any;
}
"""
get_users_types = r"""
/**
* @returns {WebUser[]}
*/
getUsers(): WebUser[];
}
"""
def fix_types(text): def fix_types(text):
text = text.replace(get_tokens_str, get_tokens_str_types) text = text.replace(get_tokens_str, get_tokens_str_types)
text = text.replace(update_style_str, update_style_str_types) text = text.replace(update_style_str, update_style_str_types)
@@ -215,6 +245,8 @@ def fix_types(text):
text = text.replace(clipboard, clipboard_types) text = text.replace(clipboard, clipboard_types)
text = text.replace(paste_from_clipboard, paste_from_clipboard_types) text = text.replace(paste_from_clipboard, paste_from_clipboard_types)
text = text.replace(defined_name_list, defined_name_list_types) text = text.replace(defined_name_list, defined_name_list_types)
text = text.replace(set_users, set_users_types)
text = text.replace(get_users, get_users_types)
with open("types.ts") as f: with open("types.ts") as f:
types_str = f.read() types_str = f.read()
header_types = "{}\n\n{}".format(header, types_str) header_types = "{}\n\n{}".format(header, types_str)

View File

@@ -6,7 +6,7 @@ use wasm_bindgen::{
use ironcalc_base::{ use ironcalc_base::{
expressions::{lexer::util::get_tokens as tokenizer, types::Area, utils::number_to_column}, expressions::{lexer::util::get_tokens as tokenizer, types::Area, utils::number_to_column},
types::{CellType, Style}, types::{CellType, Style, WebUser},
BorderArea, ClipboardData, UserModel as BaseModel, BorderArea, ClipboardData, UserModel as BaseModel,
}; };
@@ -672,4 +672,18 @@ impl Model {
.delete_defined_name(name, scope) .delete_defined_name(name, scope)
.map_err(|e| to_js_error(e.to_string())) .map_err(|e| to_js_error(e.to_string()))
} }
#[wasm_bindgen(js_name = "setUsers")]
pub fn set_users(&mut self, users: JsValue) -> Result<(), JsError> {
let users: Vec<WebUser> =
serde_wasm_bindgen::from_value(users).map_err(|e| to_js_error(e.to_string()))?;
self.model.set_users(&users);
Ok(())
}
#[wasm_bindgen(js_name = "getUsers")]
pub fn get_users(&self) -> Result<JsValue, JsError> {
let users = self.model.get_model().workbook.users.clone();
serde_wasm_bindgen::to_value(&users).map_err(|e| to_js_error(e.to_string()))
}
} }

View File

@@ -233,4 +233,11 @@ export interface DefinedName {
name: string; name: string;
scope?: number; scope?: number;
formula: string; formula: string;
}
export interface WebUser {
id: string;
sheet: number;
row: number;
column: number;
} }

View File

@@ -1,16 +1,16 @@
{ {
"name": "@ironcalc/workbook", "name": "@ironcalc/workbook",
"version": "0.5.1", "version": "0.5.4",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@ironcalc/workbook", "name": "@ironcalc/workbook",
"version": "0.5.1", "version": "0.5.4",
"dependencies": { "dependencies": {
"@emotion/react": "^11.14.0", "@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.0", "@emotion/styled": "^11.14.0",
"@ironcalc/wasm": "0.5.0", "@ironcalc/wasm": "0.5.3",
"@mui/material": "^6.4", "@mui/material": "^6.4",
"@mui/system": "^6.4", "@mui/system": "^6.4",
"i18next": "^23.11.1", "i18next": "^23.11.1",
@@ -43,11 +43,6 @@
"react-dom": "^18.0.0 || ^19.0.0" "react-dom": "^18.0.0 || ^19.0.0"
} }
}, },
"../../bindings/wasm/pkg": {
"name": "@ironcalc/wasm",
"version": "0.5.0",
"license": "MIT/Apache-2.0"
},
"node_modules/@adobe/css-tools": { "node_modules/@adobe/css-tools": {
"version": "4.4.2", "version": "4.4.2",
"resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.2.tgz", "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.2.tgz",
@@ -1060,8 +1055,10 @@
} }
}, },
"node_modules/@ironcalc/wasm": { "node_modules/@ironcalc/wasm": {
"resolved": "../../bindings/wasm/pkg", "version": "0.5.3",
"link": true "resolved": "https://registry.npmjs.org/@ironcalc/wasm/-/wasm-0.5.3.tgz",
"integrity": "sha512-ryQKR5ISkSQnnsxBYDnrAUN+GDiAQUx0MzkVpJr7VQXiymOSMZbHfpv5geum1eSJV4gw1ft69syuNolIhVZ4Hg==",
"license": "MIT/Apache-2.0"
}, },
"node_modules/@isaacs/cliui": { "node_modules/@isaacs/cliui": {
"version": "8.0.2", "version": "8.0.2",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@ironcalc/workbook", "name": "@ironcalc/workbook",
"version": "0.5.1", "version": "0.5.4",
"type": "module", "type": "module",
"main": "./dist/ironcalc.js", "main": "./dist/ironcalc.js",
"module": "./dist/ironcalc.js", "module": "./dist/ironcalc.js",
@@ -17,7 +17,7 @@
"dependencies": { "dependencies": {
"@emotion/react": "^11.14.0", "@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.0", "@emotion/styled": "^11.14.0",
"@ironcalc/wasm": "0.5.0", "@ironcalc/wasm": "0.5.3",
"@mui/material": "^6.4", "@mui/material": "^6.4",
"@mui/system": "^6.4", "@mui/system": "^6.4",
"i18next": "^23.11.1", "i18next": "^23.11.1",

View File

@@ -11,6 +11,10 @@ interface IronCalcProperties {
} }
function IronCalc(properties: IronCalcProperties) { function IronCalc(properties: IronCalcProperties) {
properties.model.setUsers([
{ id: "john@doe.com", sheet: 0, row: 5, column: 6 },
{ id: "micheal@doe.com", sheet: 0, row: 1, column: 6 },
]);
return ( return (
<ThemeProvider theme={theme}> <ThemeProvider theme={theme}>
<Workbook model={properties.model} workbookState={new WorkbookState()} /> <Workbook model={properties.model} workbookState={new WorkbookState()} />

View File

@@ -19,6 +19,13 @@ import {
outlineColor, outlineColor,
} from "./constants"; } from "./constants";
export interface UserSelection {
userId: string;
color: string;
selection: [number, number, number, number, number]; // [sheet, rowStart, columnStart, rowEnd, columnEnd]
div: HTMLDivElement;
}
export interface CanvasSettings { export interface CanvasSettings {
model: Model; model: Model;
width: number; width: number;
@@ -1244,6 +1251,34 @@ export default class WorksheetCanvas {
editor.style.height = `${height - 1}px`; editor.style.height = `${height - 1}px`;
} }
private drawUsersSelection(): void {
const users = this.model.getUsers();
for (const handle of document.querySelectorAll(
".user-selection-ironcalc",
))
handle.remove();
const colors = [];
users.forEach((user, index) => {
const { sheet, row, column } = user;
if (sheet !== this.model.getSelectedSheet()) {
return;
}
const [x, y] = this.getCoordinatesByCell(row, column);
const width = this.getColumnWidth(sheet, column);
const height = this.getRowHeight(sheet, row);
const div = document.createElement("div");
const color = getColor(index + 1);
div.className = "user-selection-ironcalc";
div.style.left = `${x}px`;
div.style.top = `${y}px`;
div.style.width = `${width}px`;
div.style.height = `${height}px`;
div.style.border = `1px solid ${color}`;
div.style.position = "absolute";
this.canvas.parentElement?.appendChild(div);
});
}
private drawCellOutline(): void { private drawCellOutline(): void {
const { cellOutline, areaOutline, cellOutlineHandle } = this; const { cellOutline, areaOutline, cellOutlineHandle } = this;
if (this.workbookState.getEditingCell()) { if (this.workbookState.getEditingCell()) {
@@ -1595,6 +1630,7 @@ export default class WorksheetCanvas {
context.stroke(); context.stroke();
this.drawCellOutline(); this.drawCellOutline();
this.drawUsersSelection();
this.drawCellEditor(); this.drawCellEditor();
this.drawExtendToArea(); this.drawExtendToArea();
this.drawActiveRanges(topLeftCell, bottomRightCell); this.drawActiveRanges(topLeftCell, bottomRightCell);

View File

@@ -110,6 +110,7 @@ fn load_xlsx_from_reader<R: Read + std::io::Seek>(
metadata, metadata,
tables, tables,
views, views,
users: Vec::new(),
}) })
} }