From 6de904b8876f920f287b63a95934c479acf78307 Mon Sep 17 00:00:00 2001 From: Kuba Sekowski Date: Fri, 20 Feb 2026 13:18:49 +0100 Subject: [PATCH 1/5] Deploying fix to the documentation (#1622) * Fix package-lock file * Docs: remove CodeSandbox embedded demos and add links to working exa,ples in Stackblitz (#1621) --- docs/guide/custom-functions.md | 10 +- docs/guide/integration-with-angular.md | 8 +- docs/guide/integration-with-react.md | 8 +- docs/guide/integration-with-svelte.md | 8 +- docs/guide/integration-with-vue.md | 10 +- package-lock.json | 224 +++++++++++++++++++++++++ 6 files changed, 231 insertions(+), 37 deletions(-) diff --git a/docs/guide/custom-functions.md b/docs/guide/custom-functions.md index d78408e61..10d1db153 100644 --- a/docs/guide/custom-functions.md +++ b/docs/guide/custom-functions.md @@ -358,18 +358,12 @@ it('returns a VALUE error if the range argument contains a string', () => { ## Working demo +Explore the full working example on [Stackblitz](https://stackblitz.com/github/handsontable/hyperformula-demos/tree/3.2.x/custom-functions?v=${$page.buildDateURIEncoded}). + This demo contains the implementation of both the [`GREET`](#add-a-simple-custom-function) and [`DOUBLE_RANGE`](#advanced-custom-function-example) custom functions. - - ## Function options You can set the following options for your function: diff --git a/docs/guide/integration-with-angular.md b/docs/guide/integration-with-angular.md index 1991fe3fb..8f78e2097 100644 --- a/docs/guide/integration-with-angular.md +++ b/docs/guide/integration-with-angular.md @@ -6,10 +6,4 @@ For more details, see the [client-side installation](client-side-installation.md ## Demo - +Explore the full working example on [Stackblitz](https://stackblitz.com/github/handsontable/hyperformula-demos/tree/3.2.x/angular-demo?v=${$page.buildDateURIEncoded}). diff --git a/docs/guide/integration-with-react.md b/docs/guide/integration-with-react.md index 75b4c64e7..d4bc7fe75 100644 --- a/docs/guide/integration-with-react.md +++ b/docs/guide/integration-with-react.md @@ -6,10 +6,4 @@ For more details, see the [client-side installation](client-side-installation.md ## Demo - +Explore the full working example on [Stackblitz](https://stackblitz.com/github/handsontable/hyperformula-demos/tree/3.2.x/react-demo?v=${$page.buildDateURIEncoded}). diff --git a/docs/guide/integration-with-svelte.md b/docs/guide/integration-with-svelte.md index 310fc8823..8b3a5f4b6 100644 --- a/docs/guide/integration-with-svelte.md +++ b/docs/guide/integration-with-svelte.md @@ -6,10 +6,4 @@ For more details, see the [client-side installation](client-side-installation.md ## Demo - +Explore the full working example on [Stackblitz](https://stackblitz.com/github/handsontable/hyperformula-demos/tree/3.2.x/svelte-demo?v=${$page.buildDateURIEncoded}). diff --git a/docs/guide/integration-with-vue.md b/docs/guide/integration-with-vue.md index 5307780c6..eaa104e0e 100644 --- a/docs/guide/integration-with-vue.md +++ b/docs/guide/integration-with-vue.md @@ -31,14 +31,8 @@ This function prevents Vue from converting the HyperFormula instance into a reac ## Demo +Explore the full working example on [Stackblitz](https://stackblitz.com/github/handsontable/hyperformula-demos/tree/3.2.x/vue-3-demo?v=${$page.buildDateURIEncoded}). + ::: tip This demo uses the [Vue 3](https://v3.vuejs.org/) framework. If you are looking for an example using Vue 2, check out the [code on GitHub](https://github.com/handsontable/hyperformula-demos/tree/2.5.x/vue-demo). ::: - - diff --git a/package-lock.json b/package-lock.json index f2d4c6f11..716278366 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10062,6 +10062,34 @@ "esbuild-windows-arm64": "0.14.7" } }, + "node_modules/esbuild-android-arm64": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.7.tgz", + "integrity": "sha512-9/Q1NC4JErvsXzJKti0NHt+vzKjZOgPIjX/e6kkuCzgfT/GcO3FVBcGIv4HeJG7oMznE6KyKhvLrFgt7CdU2/w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/esbuild-darwin-64": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.7.tgz", + "integrity": "sha512-Z9X+3TT/Xj+JiZTVlwHj2P+8GoiSmUnGVz0YZTSt8WTbW3UKw5Pw2ucuJ8VzbD2FPy0jbIKJkko/6CMTQchShQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, "node_modules/esbuild-darwin-arm64": { "version": "0.14.7", "cpu": [ @@ -10074,6 +10102,202 @@ "darwin" ] }, + "node_modules/esbuild-freebsd-64": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.7.tgz", + "integrity": "sha512-76zy5jAjPiXX/S3UvRgG85Bb0wy0zv/J2lel3KtHi4V7GUTBfhNUPt0E5bpSXJ6yMT7iThhnA5rOn+IJiUcslQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/esbuild-freebsd-arm64": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.7.tgz", + "integrity": "sha512-lSlYNLiqyzd7qCN5CEOmLxn7MhnGHPcu5KuUYOG1i+t5A6q7LgBmfYC9ZHJBoYyow3u4CNu79AWHbvVLpE/VQQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/esbuild-linux-32": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.7.tgz", + "integrity": "sha512-Vk28u409wVOXqTaT6ek0TnfQG4Ty1aWWfiysIaIRERkNLhzLhUf4i+qJBN8mMuGTYOkE40F0Wkbp6m+IidOp2A==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/esbuild-linux-64": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.7.tgz", + "integrity": "sha512-+Lvz6x+8OkRk3K2RtZwO+0a92jy9si9cUea5Zoru4yJ/6EQm9ENX5seZE0X9DTwk1dxJbjmLsJsd3IoowyzgVg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/esbuild-linux-arm": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.7.tgz", + "integrity": "sha512-OzpXEBogbYdcBqE4uKynuSn5YSetCvK03Qv1HcOY1VN6HmReuatjJ21dCH+YPHSpMEF0afVCnNfffvsGEkxGJQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/esbuild-linux-arm64": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.7.tgz", + "integrity": "sha512-kJd5beWSqteSAW086qzCEsH6uwpi7QRIpzYWHzEYwKKu9DiG1TwIBegQJmLpPsLp4v5RAFjea0JAmAtpGtRpqg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/esbuild-linux-mips64le": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.7.tgz", + "integrity": "sha512-mFWpnDhZJmj/h7pxqn1GGDsKwRfqtV7fx6kTF5pr4PfXe8pIaTERpwcKkoCwZUkWAOmUEjMIUAvFM72A6hMZnA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/esbuild-linux-ppc64le": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.7.tgz", + "integrity": "sha512-wM7f4M0bsQXfDL4JbbYD0wsr8cC8KaQ3RPWc/fV27KdErPW7YsqshZZSjDV0kbhzwpNNdhLItfbaRT8OE8OaKA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/esbuild-netbsd-64": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.7.tgz", + "integrity": "sha512-J/afS7woKyzGgAL5FlgvMyqgt5wQ597lgsT+xc2yJ9/7BIyezeXutXqfh05vszy2k3kSvhLesugsxIA71WsqBw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ] + }, + "node_modules/esbuild-openbsd-64": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.7.tgz", + "integrity": "sha512-7CcxgdlCD+zAPyveKoznbgr3i0Wnh0L8BDGRCjE/5UGkm5P/NQko51tuIDaYof8zbmXjjl0OIt9lSo4W7I8mrw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/esbuild-sunos-64": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.7.tgz", + "integrity": "sha512-GKCafP2j/KUljVC3nesw1wLFSZktb2FGCmoT1+730zIF5O6hNroo0bSEofm6ZK5mNPnLiSaiLyRB9YFgtkd5Xg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ] + }, + "node_modules/esbuild-windows-32": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.7.tgz", + "integrity": "sha512-5I1GeL/gZoUUdTPA0ws54bpYdtyeA2t6MNISalsHpY269zK8Jia/AXB3ta/KcDHv2SvNwabpImeIPXC/k0YW6A==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/esbuild-windows-64": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.7.tgz", + "integrity": "sha512-CIGKCFpQOSlYsLMbxt8JjxxvVw9MlF1Rz2ABLVfFyHUF5OeqHD5fPhGrCVNaVrhO8Xrm+yFmtjcZudUGr5/WYQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/esbuild-windows-arm64": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.7.tgz", + "integrity": "sha512-eOs1eSivOqN7cFiRIukEruWhaCf75V0N8P0zP7dh44LIhLl8y6/z++vv9qQVbkBm5/D7M7LfCfCTmt1f1wHOCw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/escalade": { "version": "3.2.0", "dev": true, From 9a510a2acb97c3d3490f9e3b9e961a1c4a98b9ad Mon Sep 17 00:00:00 2001 From: Kuba Sekowski Date: Wed, 15 Apr 2026 18:50:44 +0200 Subject: [PATCH 2/5] Deploy docs: AI integration landing pages (#1656) > [!NOTE] > **Low Risk** > Low risk documentation-only changes: adds new guide pages and adjusts VuePress sidebar navigation with no runtime or API impact. > > **Overview** > Adds three new AI-focused documentation pages: `ai-sdk`, `integration-with-langchain`, and `mcp-server`, describing how to use HyperFormula for deterministic spreadsheet computation in agent workflows. > > Updates the VuePress guide sidebar to surface these pages under **Integrations**, renames the section from *Framework integration* to *Integrations*, and moves the former *Overview* links into a new *About* section. > > Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 54c541b8f4f6b611dfa4bfd882630712b3523db2. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot). Co-authored-by: GreenFlux Co-authored-by: Claude Opus 4.6 --- docs/.vuepress/config.js | 27 ++++++------ docs/guide/ai-sdk.md | 51 +++++++++++++++++++++++ docs/guide/integration-with-langchain.md | 53 ++++++++++++++++++++++++ docs/guide/mcp-server.md | 44 ++++++++++++++++++++ 4 files changed, 163 insertions(+), 12 deletions(-) create mode 100644 docs/guide/ai-sdk.md create mode 100644 docs/guide/integration-with-langchain.md create mode 100644 docs/guide/mcp-server.md diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index 46e040b9a..70456f0da 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -175,17 +175,6 @@ module.exports = { ['/guide/demo', 'Demo'], ] }, - { - title: 'Overview', - collapsable: false, - children: [ - ['/guide/quality', 'Quality'], - ['/guide/supported-browsers', 'Supported browsers'], - ['/guide/dependencies', 'Dependencies'], - ['/guide/licensing', 'Licensing'], - ['/guide/support', 'Support'], - ] - }, { title: 'Getting started', collapsable: false, @@ -199,13 +188,16 @@ module.exports = { ] }, { - title: 'Framework integration', + title: 'Integrations', collapsable: false, children: [ ['/guide/integration-with-react', 'Integration with React'], ['/guide/integration-with-vue', 'Integration with Vue'], ['/guide/integration-with-angular', 'Integration with Angular'], ['/guide/integration-with-svelte', 'Integration with Svelte'], + ['/guide/ai-sdk', 'HyperFormula AI SDK'], + ['/guide/integration-with-langchain', 'Integration with LangChain'], + ['/guide/mcp-server', 'HyperFormula MCP Server'], ] }, { @@ -276,6 +268,17 @@ module.exports = { ['/guide/migration-from-2.x-to-3.0', 'Migrating from 2.x to 3.0'], ] }, + { + title: 'About', + collapsable: false, + children: [ + ['/guide/quality', 'Quality'], + ['/guide/supported-browsers', 'Supported browsers'], + ['/guide/dependencies', 'Dependencies'], + ['/guide/licensing', 'Licensing'], + ['/guide/support', 'Support'], + ] + }, { title: 'Miscellaneous', collapsable: false, diff --git a/docs/guide/ai-sdk.md b/docs/guide/ai-sdk.md new file mode 100644 index 000000000..aeb93e6ca --- /dev/null +++ b/docs/guide/ai-sdk.md @@ -0,0 +1,51 @@ +# HyperFormula AI SDK + +Let LLMs safely read/write spreadsheets and compute formulas via a deterministic engine. + +## What it does + +- **Evaluate formulas on the fly** —call `calculateFormula()` to evaluate any Excel-compatible formula without placing it in a cell. +- **Read and write cells and ranges** —get or set individual cells and multi-cell ranges so an LLM can inspect, populate, or modify sheet data programmatically. +- **Trace dependencies** —call `getCellDependents()` and `getCellPrecedents()` to understand which cells feed into a formula and what downstream values would change. + +## Quickstart + +```js +import HyperFormula from 'hyperformula'; +import { createSpreadsheetTools } from 'hyperformula/ai'; + +// 1. Create a HyperFormula instance with initial data +const hf = HyperFormula.buildFromArray([ + ['Revenue', 100], + ['Cost', 60], + ['Profit', '=B1-B2'], +]); + +// 2. Create tools your LLM agent can call +const tools = createSpreadsheetTools(hf); + +// 3. Agent interaction examples +tools.evaluate({ formula: '=IRR({-1000,300,400,500,200})' }); +// → 0.1189 — deterministic, no LLM math + +tools.setCellContents({ sheet: 0, col: 1, row: 0, value: 200 }); +tools.getRange({ sheet: 0, startCol: 0, startRow: 0, endCol: 1, endRow: 2 }); +// → [['Revenue', 200], ['Cost', 60], ['Profit', 140]] + +// Agent: "What drives the profit number?" +tools.getDependents({ sheet: 0, col: 1, row: 0 }); +// → [{ sheet: 0, col: 1, row: 2 }] — Revenue flows into Profit +``` + +## Use cases + +- **Explain a sheet** —ask an agent to summarize what a spreadsheet does, which cells are inputs, and how outputs are derived. +- **Generate a what-if scenario** —let the model tweak assumptions (price, volume, rate) and observe how results change in real time. +- **Validate and clean data** —have the agent scan ranges for errors, missing values, or inconsistencies and fix them with formulas or direct edits. +- **Create formulas from natural language** —describe a calculation in plain English and let the model write and verify the correct Excel formula. + +## Beta access + +::: tip +[Sign up for beta access](https://2fmjvg.share-eu1.hsforms.com/2e6drCkuLTn-1RuiYB91eJA) +::: diff --git a/docs/guide/integration-with-langchain.md b/docs/guide/integration-with-langchain.md new file mode 100644 index 000000000..cbb695dcd --- /dev/null +++ b/docs/guide/integration-with-langchain.md @@ -0,0 +1,53 @@ +# Integration with LangChain/LangGraph + +A LangChain/LangGraph tool that gives AI agents deterministic, Excel-compatible formula evaluation instead of relying on LLM-generated math. + +## What it does + +**Without HyperFormula:** + +```python +result = llm.invoke( + "Calculate the IRR for these cash flows: [-1000, 300, 400, 500, 200]" +) +# "The IRR is approximately 12.4%" ← non-deterministic, unverifiable +``` + +**With HyperFormula tool:** + +```python +from langchain_core.tools import tool +from hyperformula import HyperFormula + +hf = HyperFormula.build_from_array([[-1000, 300, 400, 500, 200]]) + +@tool +def evaluate_formula(formula: str) -> str: + """Evaluate an Excel-compatible formula using HyperFormula.""" + return hf.calculate_formula(formula, sheet_id=0) + +agent = create_react_agent(llm, [evaluate_formula]) + +# Agent calls: evaluate_formula("=IRR(A1:E1)") +# → 0.1189 ← deterministic, auditable +``` + +## How it works + +1. **Agent populates a HyperFormula sheet** —writes data and formulas (`=SUM`, `=IF`, `=VLOOKUP`, etc.) into cells. +2. **HyperFormula evaluates deterministically** —resolves the full dependency graph using 400+ built-in functions. No LLM in the loop for math. +3. **Agent continues with verified data** —computed values flow back into the chain for reasoning, reporting, or downstream actions. + +## Use cases + +- Financial modeling (NPV, IRR, amortization) +- Data transformation and aggregation (SUMIF, VLOOKUP) +- Dynamic pricing with formula-defined logic +- What-if scenarios and forecasting +- Report generation with verified KPIs + +## Beta access + +::: tip +[Sign up for beta access](https://2fmjvg.share-eu1.hsforms.com/2e6drCkuLTn-1RuiYB91eJA) +::: diff --git a/docs/guide/mcp-server.md b/docs/guide/mcp-server.md new file mode 100644 index 000000000..618323f22 --- /dev/null +++ b/docs/guide/mcp-server.md @@ -0,0 +1,44 @@ +# HyperFormula MCP Server + +An MCP (Model Context Protocol) server that exposes HyperFormula as a tool for any MCP-compatible AI client, giving LLMs deterministic spreadsheet computation. + +## What it does + +- **Evaluate formulas** —any MCP client can call HyperFormula to evaluate Excel-compatible formulas and get exact results. +- **Read and write cells** —get or set individual cell values and ranges through standard MCP tool calls. +- **Inspect dependencies** —trace which cells a formula depends on and understand the calculation graph. + +**Without HyperFormula:** + +``` +User: What's the NPV at 8% for these cash flows? +Agent: "Approximately $142.50" ← non-deterministic, unverifiable +``` + +**With HyperFormula MCP server:** + +``` +User: What's the NPV at 8% for these cash flows? +Agent → tool call: evaluate("=NPV(0.08, B1:B5)") +Agent: "$138.43" ← deterministic, auditable +``` + +## How it works + +1. **Start the MCP server** —runs HyperFormula as a local MCP server that any compatible client (Claude Desktop, Cursor, VS Code, etc.) can connect to. +2. **Client sends tool calls** —the AI client calls tools like `evaluate`, `getCellValue`, and `setCellContents` via the MCP protocol. +3. **HyperFormula evaluates deterministically** —resolves formulas using 400+ built-in functions with full dependency tracking. No LLM in the loop for math. +4. **Results flow back to the client** —computed values return through MCP, grounding the AI's response in verified numbers. + +## Use cases + +- Spreadsheet Q&A in Claude Desktop or other MCP clients +- Formula evaluation in IDE-based AI assistants +- Financial calculations in chat-based agent workflows +- Data validation and transformation via natural language + +## Beta access + +::: tip +[Sign up for beta access](https://2fmjvg.share-eu1.hsforms.com/2e6drCkuLTn-1RuiYB91eJA) +::: From 44e5ae205600ee79d20acfee8b50072fc1ad96f2 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 2 Jun 2026 11:22:43 +0000 Subject: [PATCH 3/5] docs: clarify setRowOrder/setColumnOrder permutation semantics The existing examples for setRowOrder and setColumnOrder used self-inverse permutations like [2, 1, 0] or [0, 3, 2, 1]. These produce the same output under either interpretation of the permutation argument: A. newOrder[i] = new position for the row/column currently at index i B. newOrder[i] = previous position of the row/column ending up at index i The implementation uses interpretation A, but the old examples did not disambiguate, leading users to assume interpretation B (issue #1668). This change replaces the examples with non-self-inverse cyclic shifts ([1, 2, 0] on a 3-element sheet) and adds explicit prose explaining the semantics in both the JSDoc and the guide pages. Updates apply to: - API JSDoc for setRowOrder, isItPossibleToSetRowOrder, setColumnOrder and isItPossibleToSetColumnOrder in src/HyperFormula.ts - docs/guide/sorting-data.md (intro, row and column step-by-step sections, with new warning callouts) - docs/guide/basic-operations.md (Reordering rows and columns subsections) - CHANGELOG.md entry under [Unreleased] Co-authored-by: Kuba Sekowski --- CHANGELOG.md | 4 ++ docs/guide/basic-operations.md | 12 ++-- docs/guide/sorting-data.md | 103 +++++++++++++++++---------------- src/HyperFormula.ts | 52 +++++++++++------ 4 files changed, 100 insertions(+), 71 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9cf79c677..edb5c584f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ## [Unreleased] +### Changed + +- Clarified the documentation for `setRowOrder` and `setColumnOrder` so the meaning of the permutation argument is unambiguous. [#1668](https://github.com/handsontable/hyperformula/issues/1668) + ## [3.3.0] - 2026-05-20 ### Added diff --git a/docs/guide/basic-operations.md b/docs/guide/basic-operations.md index 20a4b1edc..f6c25fd7c 100644 --- a/docs/guide/basic-operations.md +++ b/docs/guide/basic-operations.md @@ -151,11 +151,13 @@ You can change the order of rows by using the [`setRowOrder`](../api/classes/hyp * Sheet ID * [New row order](../api/classes/hyperformula.md#setroworder) +The new row order is a permutation of the form `[ newPositionForRow0, newPositionForRow1, newPositionForRow2, ... ]`. The value at index `i` is the new position for the row that is currently at index `i`. See the [Sorting data](sorting-data.md) guide for details. + This method returns [an array of changed cells](#changes-array). ```javascript -// row 0 and row 2 swap places -const changes = hfInstance.setRowOrder(0, [2, 1, 0]); +// move row 0 to position 1, row 1 to position 2, and row 2 to position 0 +const changes = hfInstance.setRowOrder(0, [1, 2, 0]); ``` ## Columns @@ -212,11 +214,13 @@ You can change the order of columns by using the [`setColumnOrder`](../api/class * Sheet ID * [New column order](../api/classes/hyperformula.md#setcolumnorder) +The new column order is a permutation of the form `[ newPositionForColumn0, newPositionForColumn1, newPositionForColumn2, ... ]`. The value at index `i` is the new position for the column that is currently at index `i`. See the [Sorting data](sorting-data.md) guide for details. + This method returns [an array of changed cells](#changes-array). ```javascript -// column 0 and column 2 swap places -const changes = hfInstance.setColumnOrder(0, [2, 1, 0]); +// move column 0 to position 1, column 1 to position 2, and column 2 to position 0 +const changes = hfInstance.setColumnOrder(0, [1, 2, 0]); ``` ## Cells diff --git a/docs/guide/sorting-data.md b/docs/guide/sorting-data.md index 8faedd3ca..e9920cd41 100644 --- a/docs/guide/sorting-data.md +++ b/docs/guide/sorting-data.md @@ -6,32 +6,41 @@ In HyperFormula, you can sort data by reordering rows and columns. To sort data in HyperFormula, you reorder rows (or columns), by providing your preferred permutation of row (or column) indexes. -You can implement any sorting algorithm that returns an array of row or column indexes. +The permutation array has the form `[ newPositionForRow0, newPositionForRow1, newPositionForRow2, ... ]`. The value at index `i` is the new position for the row that is currently at index `i`. + +You can implement any sorting algorithm that returns such an array of row or column indexes. ## Sorting rows To sort rows, use the [`isItPossibleToSetRowOrder`](../api/classes/hyperformula.md#isitpossibletosetroworder) and [`setRowOrder`](../api/classes/hyperformula.md#setroworder) methods. ### Step 1: Choose a new row order -Choose your required permutation of row indexes. +Choose your required permutation of row indexes. -For example, if you want to swap the first row with the third row, set the order to `[2, 1, 0]` instead of `[0, 1, 2]`: +For example, if you want to move the bottom row to the top of a 3-row sheet, set the order to `[1, 2, 0]` instead of `[0, 1, 2]`. This moves the row at index 0 to position 1, the row at index 1 to position 2, and the row at index 2 to position 0: ```js // a HyperFormula instance with example data const hfInstance = HyperFormula.buildFromArray([ - [1], - [2], - [4, 5], + ['A'], + ['B'], + ['C'], ]); -// we'll set the row order to [2, 1, 0] in the next steps +// we'll set the row order to [1, 2, 0] in the next steps +// the resulting sheet will be: [['C'], ['A'], ['B']] ``` ::: tip The [`setRowOrder`](../api/classes/hyperformula.md#setroworder) method accepts an array of numbers, so you can implement any function that returns an array with your required row order. ::: +::: warning +The permutation array maps **current positions** to **new positions**, not the other way around. The value at index `i` tells HyperFormula where to move the row currently at index `i`, *not* which row should end up at index `i`. + +For example, `[1, 2, 0]` means "move row 0 to position 1, row 1 to position 2, row 2 to position 0". It does **not** mean "the new row 0 comes from position 1, the new row 1 comes from position 2, ...". +::: + ### Step 2: Check if the new row order can be applied Before you change the row order, check if your specified row number permutation can actually be applied. @@ -42,16 +51,16 @@ Use the [`isItPossibleToSetRowOrder`](../api/classes/hyperformula.md#isitpossibl ```js const hfInstance = HyperFormula.buildFromArray([ - [1], - [2], - [4, 5], + ['A'], + ['B'], + ['C'], ]); // a variable to carry the user message let messageUsedInUI; // check if your permutation can be applied -const isRowOrderOk = hfInstance.isItPossibleToSetRowOrder(0, [2, 1, 0]); +const isRowOrderOk = hfInstance.isItPossibleToSetRowOrder(0, [1, 2, 0]); // display an error message if (!isRowOrderOk) { @@ -65,39 +74,35 @@ If your specified row number permutation is valid, change the row order: ```js const hfInstance = HyperFormula.buildFromArray([ - [1], - [2], - [4, 5], + ['A'], + ['B'], + ['C'], ]); let messageUsedInUI; -const isRowOrderOk = hfInstance.isItPossibleToSetRowOrder(0, [2, 1, 0]); +const isRowOrderOk = hfInstance.isItPossibleToSetRowOrder(0, [1, 2, 0]); if (!isRowOrderOk) { messageUsedInUI = 'Sorry, you cannot sort rows in this way.' } else { // set the new row order - setRowOrder(0, [2, 1, 0]); + hfInstance.setRowOrder(0, [1, 2, 0]); } -// rows 0 and 2 swap places +// the resulting sheet is: [['C'], ['A'], ['B']] -// returns: +// the method returns an array of cells whose values changed: // [{ -// address: { sheet: 0, col: 0, row: 2 }, -// newValue: 1, +// address: { sheet: 0, col: 0, row: 1 }, +// newValue: 'A', // }, // { -// address: { sheet: 0, col: 1, row: 2 }, -// newValue: null, +// address: { sheet: 0, col: 0, row: 2 }, +// newValue: 'B', // }, // { // address: { sheet: 0, col: 0, row: 0 }, -// newValue: 4, -// }, -// { -// address: { sheet: 0, col: 1, row: 0 }, -// newValue: 5, +// newValue: 'C', // }] ``` @@ -105,25 +110,31 @@ if (!isRowOrderOk) { To sort columns, use the [`isItPossibleToSetColumnOrder`](../api/classes/hyperformula.md#isitpossibletosetcolumnorder) and [`setColumnOrder`](../api/classes/hyperformula.md#setcolumnorder) methods. +The permutation array has the same shape as for rows: `[ newPositionForColumn0, newPositionForColumn1, newPositionForColumn2, ... ]`. The value at index `i` is the new position for the column that is currently at index `i`. + ### Step 1: Choose a new column order Choose your required permutation of column indexes. -For example, if you want to swap the first column with the third column, set the order to `[2, 1, 0]` instead of `[0, 1, 2]`: +For example, if you want to move the last column to the front of a 3-column sheet, set the order to `[1, 2, 0]` instead of `[0, 1, 2]`. This moves the column at index 0 to position 1, the column at index 1 to position 2, and the column at index 2 to position 0: ```js // a HyperFormula instance with example data const hfInstance = HyperFormula.buildFromArray([ - [1, 2, 4], - [5] + ['A', 'B', 'C'] ]); -// we'll set the column order to [2, 1, 0] in the next steps +// we'll set the column order to [1, 2, 0] in the next steps +// the resulting sheet will be: [['C', 'A', 'B']] ``` ::: tip The [`setColumnOrder`](../api/classes/hyperformula.md#setcolumnorder) method accepts an array of numbers, so you can implement any function that returns an array with your required column order. ::: +::: warning +The permutation array maps **current positions** to **new positions**, not the other way around. The value at index `i` tells HyperFormula where to move the column currently at index `i`, *not* which column should end up at index `i`. +::: + ### Step 2: Check if the new column order can be applied Before you change the column order, check if your specified column number permutation can actually be applied. @@ -134,15 +145,14 @@ Use the [`isItPossibleToSetColumnOrder`](../api/classes/hyperformula.md#isitposs ```js const hfInstance = HyperFormula.buildFromArray([ - [1, 2, 4], - [5] + ['A', 'B', 'C'] ]); // a variable to carry the user message let messageUsedInUI; // check if your permutation can be applied -const isColumnOrderOk = hfInstance.isItPossibleToSetColumnOrder(0, [2, 1, 0]); +const isColumnOrderOk = hfInstance.isItPossibleToSetColumnOrder(0, [1, 2, 0]); // display an error message if (!isColumnOrderOk) { @@ -156,38 +166,33 @@ If your specified column number permutation is valid, change the column order: ```js const hfInstance = HyperFormula.buildFromArray([ - [1, 2, 4], - [5] + ['A', 'B', 'C'] ]); let messageUsedInUI; -const isColumnOrderOk = hfInstance.isItPossibleToSetColumnOrder(0, [2, 1, 0]); +const isColumnOrderOk = hfInstance.isItPossibleToSetColumnOrder(0, [1, 2, 0]); if (!isColumnOrderOk) { messageUsedInUI = 'Sorry, you cannot sort columns in this way.' } else { // set the new column order - setColumnOrder(0, [2, 1, 0]); + hfInstance.setColumnOrder(0, [1, 2, 0]); } -// columns 0 and 2 swap places +// the resulting sheet is: [['C', 'A', 'B']] -//returns: +// the method returns an array of cells whose values changed: // [{ -// address: { sheet: 0, col: 2, row: 0 }, -// newValue: 1, +// address: { sheet: 0, col: 1, row: 0 }, +// newValue: 'A', // }, // { -// address: { sheet: 0, col: 2, row: 1 }, -// newValue: 5, +// address: { sheet: 0, col: 2, row: 0 }, +// newValue: 'B', // }, // { // address: { sheet: 0, col: 0, row: 0 }, -// newValue: 4, -// }, -// { -// address: { sheet: 0, col: 0, row: 1 }, -// newValue: null, +// newValue: 'C', // }] ``` diff --git a/src/HyperFormula.ts b/src/HyperFormula.ts index 566957270..f91f02505 100644 --- a/src/HyperFormula.ts +++ b/src/HyperFormula.ts @@ -1385,7 +1385,11 @@ export class HyperFormula implements TypedEmitter { /** * Reorders rows of a sheet according to a permutation of 0-based indexes. - * Parameter `newRowOrder` should have a form `[ newPositionForRow0, newPositionForRow1, newPositionForRow2, ... ]`. + * + * Parameter `newRowOrder` should have the form `[ newPositionForRow0, newPositionForRow1, newPositionForRow2, ... ]`. + * In other words, the value at index `i` is the new position for the row that is currently at index `i`. + * Note that this is the opposite of `[ previousPositionForRow0, previousPositionForRow1, ... ]`. + * * This method might be used to [sort the rows of a sheet](../../guide/sorting-data.md). * * Returns [an array of cells whose values changed as a result of this operation](/guide/basic-operations.md#changes-array). @@ -1407,15 +1411,15 @@ export class HyperFormula implements TypedEmitter { * const hfInstance = HyperFormula.buildFromArray([ * ['A'], * ['B'], - * ['C'], - * ['D'] + * ['C'] * ]); * - * const newRowOrder = [0, 3, 2, 1]; // [ newPosForA, newPosForB, newPosForC, newPosForD ] + * // Move 'A' to index 1, 'B' to index 2, and 'C' to index 0. + * const newRowOrder = [1, 2, 0]; // [ newPosForA, newPosForB, newPosForC ] * * const changes = hfInstance.setRowOrder(0, newRowOrder); * - * // Sheet after this operation: [['A'], ['D'], ['C'], ['B']] + * // Sheet after this operation: [['C'], ['A'], ['B']] * ``` * * @category Rows @@ -1429,6 +1433,10 @@ export class HyperFormula implements TypedEmitter { /** * Checks if it is possible to reorder rows of a sheet according to a permutation. * + * Parameter `newRowOrder` should have the form `[ newPositionForRow0, newPositionForRow1, newPositionForRow2, ... ]`, + * i.e. the value at index `i` is the new position for the row that is currently at index `i`. + * See [[setRowOrder]] for details. + * * @param {number} sheetId - ID of a sheet to operate on * @param {number[]} newRowOrder - permutation of rows * @@ -1437,15 +1445,15 @@ export class HyperFormula implements TypedEmitter { * @example * ```js * const hfInstance = HyperFormula.buildFromArray([ - * [1], - * [2], - * [4, 5], + * ['A'], + * ['B'], + * ['C'] * ]); * * // returns true - * hfInstance.isItPossibleToSetRowOrder(0, [2, 1, 0]); + * hfInstance.isItPossibleToSetRowOrder(0, [1, 2, 0]); * - * // returns false + * // returns false (array length must match the number of rows) * hfInstance.isItPossibleToSetRowOrder(0, [2]); * ``` * @@ -1550,7 +1558,11 @@ export class HyperFormula implements TypedEmitter { /** * Reorders columns of a sheet according to a permutation of 0-based indexes. - * Parameter `newColumnOrder` should have a form `[ newPositionForColumn0, newPositionForColumn1, newPositionForColumn2, ... ]`. + * + * Parameter `newColumnOrder` should have the form `[ newPositionForColumn0, newPositionForColumn1, newPositionForColumn2, ... ]`. + * In other words, the value at index `i` is the new position for the column that is currently at index `i`. + * Note that this is the opposite of `[ previousPositionForColumn0, previousPositionForColumn1, ... ]`. + * * This method might be used to [sort the columns of a sheet](../../guide/sorting-data.md). * * Returns [an array of cells whose values changed as a result of this operation](/guide/basic-operations.md#changes-array). @@ -1570,14 +1582,15 @@ export class HyperFormula implements TypedEmitter { * @example * ```js * const hfInstance = HyperFormula.buildFromArray([ - * ['A', 'B', 'C', 'D'] + * ['A', 'B', 'C'] * ]); * - * const newColumnOrder = [0, 3, 2, 1]; // [ newPosForA, newPosForB, newPosForC, newPosForD ] + * // Move 'A' to index 1, 'B' to index 2, and 'C' to index 0. + * const newColumnOrder = [1, 2, 0]; // [ newPosForA, newPosForB, newPosForC ] * * const changes = hfInstance.setColumnOrder(0, newColumnOrder); * - * // Sheet after this operation: [['A', 'D', 'C', 'B']] + * // Sheet after this operation: [['C', 'A', 'B']] * ``` * * @category Columns @@ -1591,6 +1604,10 @@ export class HyperFormula implements TypedEmitter { /** * Checks if it is possible to reorder columns of a sheet according to a permutation. * + * Parameter `newColumnOrder` should have the form `[ newPositionForColumn0, newPositionForColumn1, newPositionForColumn2, ... ]`, + * i.e. the value at index `i` is the new position for the column that is currently at index `i`. + * See [[setColumnOrder]] for details. + * * @param {number} sheetId - ID of a sheet to operate on * @param {number[]} newColumnOrder - permutation of columns * @@ -1599,14 +1616,13 @@ export class HyperFormula implements TypedEmitter { * @example * ```js * const hfInstance = HyperFormula.buildFromArray([ - * [1, 2, 4], - * [5] + * ['A', 'B', 'C'] * ]); * * // returns true - * hfInstance.isItPossibleToSetColumnOrder(0, [2, 1, 0]); + * hfInstance.isItPossibleToSetColumnOrder(0, [1, 2, 0]); * - * // returns false + * // returns false (array length must match the number of columns) * hfInstance.isItPossibleToSetColumnOrder(0, [1]); * ``` * From bbb62318af12e7c0862bd170214cdb5d47845094 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 2 Jun 2026 11:42:51 +0000 Subject: [PATCH 4/5] docs: drop changelog entry for doc-only change, document the convention Documentation-only changes don't need a CHANGELOG.md entry. Remove the entry added for the setRowOrder docs clarification and add a note to DEV_DOCS.md spelling out the convention so future contributors know. Co-authored-by: Kuba Sekowski --- CHANGELOG.md | 4 ---- DEV_DOCS.md | 4 ++++ 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index edb5c584f..9cf79c677 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,10 +7,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ## [Unreleased] -### Changed - -- Clarified the documentation for `setRowOrder` and `setColumnOrder` so the meaning of the permutation argument is unambiguous. [#1668](https://github.com/handsontable/hyperformula/issues/1668) - ## [3.3.0] - 2026-05-20 ### Added diff --git a/DEV_DOCS.md b/DEV_DOCS.md index 3349b9e00..766019a4f 100644 --- a/DEV_DOCS.md +++ b/DEV_DOCS.md @@ -19,6 +19,10 @@ Each change to the production code (bugfixes, new features, improvements) must i - changelog entry - pull request description +### Documentation-only changes + +Pull requests that only modify documentation (guides, JSDoc, README, etc.) and do not change runtime behavior do not require a `CHANGELOG.md` entry. + ## Sources of the function translations HF supports internationalization and provides the localized function names for all built-in languages. When looking for the valid translations for the new functions, try these sources: From ed3abc353c75b4eb8017080e928bac238bc7370c Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 2 Jun 2026 11:47:15 +0000 Subject: [PATCH 5/5] docs: inline doc-only changelog note next to the changelog entry bullet Address review feedback: move the note as a sub-bullet under '- changelog entry' instead of a separate section. Co-authored-by: Kuba Sekowski --- DEV_DOCS.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/DEV_DOCS.md b/DEV_DOCS.md index 766019a4f..d8c86de69 100644 --- a/DEV_DOCS.md +++ b/DEV_DOCS.md @@ -17,12 +17,9 @@ Each change to the production code (bugfixes, new features, improvements) must i - for breaking changes: a section in the migration guide - technical documentation in the form of the jsdoc comments (high-level description of the concepts used in the more complex code fragments) - changelog entry + - not required for documentation-only changes (guides, JSDoc, README, etc.) - pull request description -### Documentation-only changes - -Pull requests that only modify documentation (guides, JSDoc, README, etc.) and do not change runtime behavior do not require a `CHANGELOG.md` entry. - ## Sources of the function translations HF supports internationalization and provides the localized function names for all built-in languages. When looking for the valid translations for the new functions, try these sources: