From 9d83cc87c9a059f8eaff40b4c4a3ee53c396d72e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Hatcher?= Date: Sun, 7 Apr 2024 14:19:06 +0200 Subject: [PATCH] UPDATE: Adds web app --- base/src/lib.rs | 1 + base/src/new_empty.rs | 9 +- base/src/types.rs | 4 + .../{user_model.rs => user_model/common.rs} | 502 +- base/src/user_model/history.rs | 164 + base/src/user_model/mod.rs | 12 + base/src/user_model/ui.rs | 671 + bindings/wasm/fix_types.py | 32 + bindings/wasm/src/lib.rs | 101 +- bindings/wasm/types.ts | 53 +- webapp/.gitignore | 3 + webapp/.storybook/main.ts | 21 + webapp/.storybook/preview.tsx | 29 + webapp/README.md | 21 + webapp/example.ic | Bin 0 -> 5280 bytes webapp/index.html | 16 + webapp/jest.config.ts | 15 + webapp/package-lock.json | 16992 ++++++++++++++++ webapp/package.json | 57 + webapp/src/App.css | 6 + webapp/src/App.tsx | 37 + webapp/src/components/README.md | 12 + .../components/WorksheetCanvas/constants.ts | 18 + .../src/components/WorksheetCanvas/types.ts | 23 + webapp/src/components/WorksheetCanvas/util.ts | 396 + .../WorksheetCanvas/worksheetCanvas.ts | 1366 ++ webapp/src/components/borderPicker.tsx | 566 + webapp/src/components/colorPicker.tsx | 262 + webapp/src/components/editor/editor.tsx | 420 + webapp/src/components/editor/editorContext.ts | 45 + webapp/src/components/editor/index.tsx | 3 + webapp/src/components/editor/tokenTypes.ts | 92 + .../src/components/editor/useEditorKeyDown.ts | 108 + webapp/src/components/editor/util.tsx | 334 + webapp/src/components/formatMenu.tsx | 109 + webapp/src/components/formatPicker.tsx | 46 + webapp/src/components/formatUtil.ts | 36 + webapp/src/components/formulaDialog.tsx | 51 + webapp/src/components/formulabar.tsx | 132 + webapp/src/components/navigation/index.ts | 2 + webapp/src/components/navigation/menus.tsx | 122 + .../src/components/navigation/navigation.tsx | 141 + webapp/src/components/navigation/sheet.tsx | 127 + webapp/src/components/navigation/types.ts | 5 + webapp/src/components/toolbar.tsx | 445 + .../src/components/useKeyboardNavigation.ts | 229 + webapp/src/components/usePointer.ts | 227 + webapp/src/components/workbook.tsx | 377 + webapp/src/components/workbookContext.ts | 62 + webapp/src/components/workbookState.ts | 125 + webapp/src/components/worksheet.tsx | 523 + webapp/src/fonts.css | 16 + webapp/src/fonts/inter-v13-latin-600.woff2 | Bin 0 -> 22820 bytes .../src/fonts/inter-v13-latin-regular.woff2 | Bin 0 -> 21564 bytes webapp/src/i18n.ts | 18 + webapp/src/icons/arrow-middle-from-line.svg | 14 + webapp/src/icons/border-bottom.svg | 6 + webapp/src/icons/border-center-h.svg | 4 + webapp/src/icons/border-center-v.svg | 4 + webapp/src/icons/border-inner.svg | 5 + webapp/src/icons/border-left.svg | 6 + webapp/src/icons/border-none.svg | 5 + webapp/src/icons/border-outer.svg | 5 + webapp/src/icons/border-right.svg | 6 + webapp/src/icons/border-style.svg | 15 + webapp/src/icons/border-top.svg | 6 + webapp/src/icons/decrease-decimal.svg | 6 + webapp/src/icons/delete-column.svg | 10 + webapp/src/icons/delete-row.svg | 8 + webapp/src/icons/fx.svg | 3 + webapp/src/icons/increase-decimal.svg | 7 + webapp/src/icons/index.ts | 46 + webapp/src/icons/insert-column-left.svg | 7 + webapp/src/icons/insert-column-right.svg | 7 + webapp/src/icons/insert-row-above.svg | 7 + webapp/src/icons/insert-row-below.svg | 7 + webapp/src/index.css | 4 + webapp/src/locale/en_us.json | 50 + webapp/src/main.tsx | 14 + webapp/src/theme.ts | 66 + webapp/src/vite-env.d.ts | 2 + webapp/tsconfig.json | 26 + webapp/tsconfig.node.json | 10 + webapp/vite.config.ts | 14 + xlsx/src/import/mod.rs | 2 + 85 files changed, 25210 insertions(+), 346 deletions(-) rename base/src/{user_model.rs => user_model/common.rs} (84%) create mode 100644 base/src/user_model/history.rs create mode 100644 base/src/user_model/mod.rs create mode 100644 base/src/user_model/ui.rs create mode 100644 webapp/.gitignore create mode 100644 webapp/.storybook/main.ts create mode 100644 webapp/.storybook/preview.tsx create mode 100644 webapp/README.md create mode 100644 webapp/example.ic create mode 100644 webapp/index.html create mode 100644 webapp/jest.config.ts create mode 100644 webapp/package-lock.json create mode 100644 webapp/package.json create mode 100644 webapp/src/App.css create mode 100644 webapp/src/App.tsx create mode 100644 webapp/src/components/README.md create mode 100644 webapp/src/components/WorksheetCanvas/constants.ts create mode 100644 webapp/src/components/WorksheetCanvas/types.ts create mode 100644 webapp/src/components/WorksheetCanvas/util.ts create mode 100644 webapp/src/components/WorksheetCanvas/worksheetCanvas.ts create mode 100644 webapp/src/components/borderPicker.tsx create mode 100644 webapp/src/components/colorPicker.tsx create mode 100644 webapp/src/components/editor/editor.tsx create mode 100644 webapp/src/components/editor/editorContext.ts create mode 100644 webapp/src/components/editor/index.tsx create mode 100644 webapp/src/components/editor/tokenTypes.ts create mode 100644 webapp/src/components/editor/useEditorKeyDown.ts create mode 100644 webapp/src/components/editor/util.tsx create mode 100644 webapp/src/components/formatMenu.tsx create mode 100644 webapp/src/components/formatPicker.tsx create mode 100644 webapp/src/components/formatUtil.ts create mode 100644 webapp/src/components/formulaDialog.tsx create mode 100644 webapp/src/components/formulabar.tsx create mode 100644 webapp/src/components/navigation/index.ts create mode 100644 webapp/src/components/navigation/menus.tsx create mode 100644 webapp/src/components/navigation/navigation.tsx create mode 100644 webapp/src/components/navigation/sheet.tsx create mode 100644 webapp/src/components/navigation/types.ts create mode 100644 webapp/src/components/toolbar.tsx create mode 100644 webapp/src/components/useKeyboardNavigation.ts create mode 100644 webapp/src/components/usePointer.ts create mode 100644 webapp/src/components/workbook.tsx create mode 100644 webapp/src/components/workbookContext.ts create mode 100644 webapp/src/components/workbookState.ts create mode 100644 webapp/src/components/worksheet.tsx create mode 100644 webapp/src/fonts.css create mode 100644 webapp/src/fonts/inter-v13-latin-600.woff2 create mode 100644 webapp/src/fonts/inter-v13-latin-regular.woff2 create mode 100644 webapp/src/i18n.ts create mode 100644 webapp/src/icons/arrow-middle-from-line.svg create mode 100644 webapp/src/icons/border-bottom.svg create mode 100644 webapp/src/icons/border-center-h.svg create mode 100644 webapp/src/icons/border-center-v.svg create mode 100644 webapp/src/icons/border-inner.svg create mode 100644 webapp/src/icons/border-left.svg create mode 100644 webapp/src/icons/border-none.svg create mode 100644 webapp/src/icons/border-outer.svg create mode 100644 webapp/src/icons/border-right.svg create mode 100644 webapp/src/icons/border-style.svg create mode 100644 webapp/src/icons/border-top.svg create mode 100644 webapp/src/icons/decrease-decimal.svg create mode 100644 webapp/src/icons/delete-column.svg create mode 100644 webapp/src/icons/delete-row.svg create mode 100644 webapp/src/icons/fx.svg create mode 100644 webapp/src/icons/increase-decimal.svg create mode 100644 webapp/src/icons/index.ts create mode 100644 webapp/src/icons/insert-column-left.svg create mode 100644 webapp/src/icons/insert-column-right.svg create mode 100644 webapp/src/icons/insert-row-above.svg create mode 100644 webapp/src/icons/insert-row-below.svg create mode 100644 webapp/src/index.css create mode 100644 webapp/src/locale/en_us.json create mode 100644 webapp/src/main.tsx create mode 100644 webapp/src/theme.ts create mode 100644 webapp/src/vite-env.d.ts create mode 100644 webapp/tsconfig.json create mode 100644 webapp/tsconfig.node.json create mode 100644 webapp/vite.config.ts diff --git a/base/src/lib.rs b/base/src/lib.rs index a5c632b..8db4ceb 100644 --- a/base/src/lib.rs +++ b/base/src/lib.rs @@ -58,3 +58,4 @@ pub mod mock_time; pub use model::get_milliseconds_since_epoch; pub use model::Model; pub use user_model::UserModel; +pub use user_model::BorderArea; diff --git a/base/src/new_empty.rs b/base/src/new_empty.rs index 3b01cc4..3167f92 100644 --- a/base/src/new_empty.rs +++ b/base/src/new_empty.rs @@ -353,7 +353,14 @@ impl Model { let now = dt.format("%Y-%m-%dT%H:%M:%SZ").to_string(); let mut views = HashMap::new(); - views.insert(0, WorkbookView { sheet: 0 }); + views.insert( + 0, + WorkbookView { + sheet: 0, + window_width: 800, + window_height: 600, + }, + ); // String versions of the locale are added here to simplify the serialize/deserialize logic let workbook = Workbook { diff --git a/base/src/types.rs b/base/src/types.rs index ebda7c2..b62cdb5 100644 --- a/base/src/types.rs +++ b/base/src/types.rs @@ -33,6 +33,10 @@ pub struct WorkbookSettings { pub struct WorkbookView { /// The index of the currently selected sheet. pub sheet: u32, + /// The current width of the window + pub window_width: i64, + /// The current heigh of the window + pub window_height: i64, } /// An internal representation of an IronCalc Workbook diff --git a/base/src/user_model.rs b/base/src/user_model/common.rs similarity index 84% rename from base/src/user_model.rs rename to base/src/user_model/common.rs index 9351f53..01ffbaf 100644 --- a/base/src/user_model.rs +++ b/base/src/user_model/common.rs @@ -2,7 +2,6 @@ use std::{collections::HashMap, fmt::Debug}; -use bitcode::{Decode, Encode}; use serde::{Deserialize, Serialize}; use crate::{ @@ -13,180 +12,35 @@ use crate::{ }, model::Model, types::{ - Alignment, BorderItem, BorderStyle, Cell, CellType, Col, HorizontalAlignment, Row, - SheetProperties, Style, VerticalAlignment, + Alignment, BorderItem, BorderStyle, CellType, Col, HorizontalAlignment, SheetProperties, + Style, VerticalAlignment, }, utils::is_valid_hex_color, }; +use crate::user_model::history::{ + ColumnData, Diff, DiffList, DiffType, History, QueueDiffs, RowData, +}; + #[derive(Serialize, Deserialize)] -#[cfg_attr(test, derive(PartialEq, Debug))] -pub struct SelectedView { - pub sheet: u32, - pub row: i32, - pub column: i32, - pub range: [i32; 4], - pub top_row: i32, - pub left_column: i32, +pub enum BorderType { + All, + Inner, + Outer, + Top, + Right, + Bottom, + Left, + CenterH, + CenterV, + None, } -#[derive(Clone, Encode, Decode)] -struct RowData { - row: Option, - data: HashMap, -} - -#[derive(Clone, Encode, Decode)] -struct ColumnData { - column: Option, - data: HashMap, -} - -#[derive(Clone, Encode, Decode)] -enum Diff { - // Cell diffs - SetCellValue { - sheet: u32, - row: i32, - column: i32, - new_value: String, - old_value: Box>, - }, - CellClearContents { - sheet: u32, - row: i32, - column: i32, - old_value: Box>, - }, - CellClearAll { - sheet: u32, - row: i32, - column: i32, - old_value: Box>, - old_style: Box + + + + + + diff --git a/webapp/src/icons/border-top.svg b/webapp/src/icons/border-top.svg new file mode 100644 index 0000000..9051f73 --- /dev/null +++ b/webapp/src/icons/border-top.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/webapp/src/icons/decrease-decimal.svg b/webapp/src/icons/decrease-decimal.svg new file mode 100644 index 0000000..ab46d1e --- /dev/null +++ b/webapp/src/icons/decrease-decimal.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/webapp/src/icons/delete-column.svg b/webapp/src/icons/delete-column.svg new file mode 100644 index 0000000..36fc423 --- /dev/null +++ b/webapp/src/icons/delete-column.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/webapp/src/icons/delete-row.svg b/webapp/src/icons/delete-row.svg new file mode 100644 index 0000000..ffebabf --- /dev/null +++ b/webapp/src/icons/delete-row.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/webapp/src/icons/fx.svg b/webapp/src/icons/fx.svg new file mode 100644 index 0000000..fb9e203 --- /dev/null +++ b/webapp/src/icons/fx.svg @@ -0,0 +1,3 @@ + + + diff --git a/webapp/src/icons/increase-decimal.svg b/webapp/src/icons/increase-decimal.svg new file mode 100644 index 0000000..c8ffa01 --- /dev/null +++ b/webapp/src/icons/increase-decimal.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/webapp/src/icons/index.ts b/webapp/src/icons/index.ts new file mode 100644 index 0000000..cad60ef --- /dev/null +++ b/webapp/src/icons/index.ts @@ -0,0 +1,46 @@ +import DecimalPlacesDecreaseIcon from "./decrease-decimal.svg?react"; +import DecimalPlacesIncreaseIcon from "./increase-decimal.svg?react"; + +import BorderBottomIcon from "./border-bottom.svg?react"; +import BorderCenterHIcon from "./border-center-h.svg?react"; +import BorderCenterVIcon from "./border-center-v.svg?react"; +import BorderInnerIcon from "./border-inner.svg?react"; +import BorderLeftIcon from "./border-left.svg?react"; +import BorderOuterIcon from "./border-outer.svg?react"; +import BorderRightIcon from "./border-right.svg?react"; +import BorderTopIcon from "./border-top.svg?react"; +import BorderNoneIcon from "./border-none.svg?react"; +import BorderStyleIcon from "./border-style.svg?react"; + +import DeleteColumnIcon from "./delete-column.svg?react"; +import DeleteRowIcon from "./delete-row.svg?react"; +import InsertColumnLeftIcon from "./insert-column-left.svg?react"; +import InsertColumnRightIcon from "./insert-column-right.svg?react"; +import InsertRowAboveIcon from "./insert-row-above.svg?react"; +import InsertRowBelow from "./insert-row-below.svg?react"; +import ArrowMiddleFromLine from "./arrow-middle-from-line.svg?react"; + +import Fx from "./fx.svg?react"; + +export { + ArrowMiddleFromLine, + DecimalPlacesDecreaseIcon, + DecimalPlacesIncreaseIcon, + BorderBottomIcon, + BorderCenterHIcon, + BorderCenterVIcon, + BorderInnerIcon, + BorderLeftIcon, + BorderOuterIcon, + BorderRightIcon, + BorderTopIcon, + BorderNoneIcon, + BorderStyleIcon, + DeleteColumnIcon, + DeleteRowIcon, + InsertColumnLeftIcon, + InsertColumnRightIcon, + InsertRowAboveIcon, + InsertRowBelow, + Fx, +}; diff --git a/webapp/src/icons/insert-column-left.svg b/webapp/src/icons/insert-column-left.svg new file mode 100644 index 0000000..1d6e56d --- /dev/null +++ b/webapp/src/icons/insert-column-left.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/webapp/src/icons/insert-column-right.svg b/webapp/src/icons/insert-column-right.svg new file mode 100644 index 0000000..5cc5178 --- /dev/null +++ b/webapp/src/icons/insert-column-right.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/webapp/src/icons/insert-row-above.svg b/webapp/src/icons/insert-row-above.svg new file mode 100644 index 0000000..336d193 --- /dev/null +++ b/webapp/src/icons/insert-row-above.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/webapp/src/icons/insert-row-below.svg b/webapp/src/icons/insert-row-below.svg new file mode 100644 index 0000000..8b7f88b --- /dev/null +++ b/webapp/src/icons/insert-row-below.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/webapp/src/index.css b/webapp/src/index.css new file mode 100644 index 0000000..ea1e941 --- /dev/null +++ b/webapp/src/index.css @@ -0,0 +1,4 @@ +body { + margin: 0; + padding: 0; +} diff --git a/webapp/src/locale/en_us.json b/webapp/src/locale/en_us.json new file mode 100644 index 0000000..d5838ea --- /dev/null +++ b/webapp/src/locale/en_us.json @@ -0,0 +1,50 @@ +{ + "toolbar": { + "redo": "Redo", + "undo": "Undo", + "copy_styles": "Copy styles", + "euro": "Format as Euro", + "percentage": "Format as Percentage", + "bold": "Bold", + "italic": "Italic", + "underline": "Underline", + "strike_through": "Strikethrough", + "align_left": "Align left", + "align_right": "Align right", + "align_center": "Align center", + "format_number": "Format number", + "font_color": "Font color", + "fill_color": "Fill color", + "borders": "Borders", + "decimal_places_increase": "Increase decimal places", + "decimal_places_decrease": "Decrease decimal places", + "format_menu": { + "auto": "Auto", + "number": "Number", + "percentage": "Percentage", + "currency_eur": "Euro (EUR)", + "currency_usd": "Dollar (USD", + "currency_gbp": "British Pound (GBD)", + "date_short": "Short date", + "date_long": "Long date", + "custom": "Custom", + "number_example": "1,000.00", + "percentage_example": "10%", + "currency_eur_example": "€", + "currency_usd_example": "$", + "currency_gbp_example": "£", + "date_short_example": "09/24/2024", + "date_long_example": "Tuesday, September 24, 2024" + } + }, + "num_fmt" :{ + "title": "Custom number format", + "label": "Number format", + "save": "Save" + }, + "sheet_rename": { + "rename": "Save", + "label": "New name", + "title": "Rename Sheet" + } +} \ No newline at end of file diff --git a/webapp/src/main.tsx b/webapp/src/main.tsx new file mode 100644 index 0000000..8239cba --- /dev/null +++ b/webapp/src/main.tsx @@ -0,0 +1,14 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import App from './App.tsx'; +import './index.css'; +import { theme } from './theme.ts'; +import ThemeProvider from '@mui/material/styles/ThemeProvider'; + +ReactDOM.createRoot(document.getElementById('root')!).render( + + + + + , +); diff --git a/webapp/src/theme.ts b/webapp/src/theme.ts new file mode 100644 index 0000000..179ca43 --- /dev/null +++ b/webapp/src/theme.ts @@ -0,0 +1,66 @@ +import { createTheme } from "@mui/material/styles"; +import './fonts.css'; + +export const theme = createTheme({ + typography: { + fontFamily: "Inter", + }, + palette: { + common: { + black: "#272525", + white: "#FFF", + }, + primary: { + main: "#F2994A", + light: "#EFAA6D", + dark: "#D68742", + contrastText: "#FFF", + }, + secondary: { + main: "#2F80ED", + light: "#4E92EC", + dark: "#2B6EC8", + contrastText: "#FFF", + }, + error: { + main: "#EB5757", + light: "#E77A7A", + dark: "#CB4C4C", + contrastText: "#FFF", + }, + warning: { + main: "#F2C94C", + light: "#EED384", + dark: "#D6B244", + contrastText: "#FFF", + }, + info: { + main: "#9E9E9E", + light: "#E0E0E0", + dark: "#757575", + contrastText: "#FFF", + }, + success: { + main: "#27AE60", + light: "#57BD82", + dark: "#239152", + contrastText: "#FFF", + }, + grey: { + "50": "#F2F2F2", + "100": "#F5F5F5", + "200": "#EEEEEE", + "300": "#E0E0E0", + "400": "#BDBDBD", + "500": "#9E9E9E", + "600": "#757575", + "700": "#616161", + "800": "#424242", + "900": "#333333", + A100: "#F2F2F2", + A200: "#EEEEEE", + A400: "#bdbdbd", + A700: "#616161", + }, + }, +}); diff --git a/webapp/src/vite-env.d.ts b/webapp/src/vite-env.d.ts new file mode 100644 index 0000000..b1f45c7 --- /dev/null +++ b/webapp/src/vite-env.d.ts @@ -0,0 +1,2 @@ +/// +/// diff --git a/webapp/tsconfig.json b/webapp/tsconfig.json new file mode 100644 index 0000000..47787c0 --- /dev/null +++ b/webapp/tsconfig.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": false, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "esModuleInterop": true + }, + "include": ["src"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/webapp/tsconfig.node.json b/webapp/tsconfig.node.json new file mode 100644 index 0000000..42872c5 --- /dev/null +++ b/webapp/tsconfig.node.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/webapp/vite.config.ts b/webapp/vite.config.ts new file mode 100644 index 0000000..60a7425 --- /dev/null +++ b/webapp/vite.config.ts @@ -0,0 +1,14 @@ +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; +import svgr from 'vite-plugin-svgr'; + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [react(), svgr()], + server: { + fs: { + // Allow serving files from one level up to the project root + allow: ['..'], + }, + }, +}); diff --git a/xlsx/src/import/mod.rs b/xlsx/src/import/mod.rs index d130237..70426d9 100644 --- a/xlsx/src/import/mod.rs +++ b/xlsx/src/import/mod.rs @@ -93,6 +93,8 @@ fn load_xlsx_from_reader( 0, WorkbookView { sheet: selected_sheet, + window_width: 800, + window_height: 600, }, ); Ok(Workbook {