Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@

---

> 🚀 **We're hiring!** Join HyperFormula team as a **Senior Software Engineer**. [See the role and apply](https://handsontable.traffit.com/public/an/4b09e1395bf8ea42ef86db4c4657992c2f48673d).

HyperFormula is a headless spreadsheet built in TypeScript, serving as both a parser and evaluator of spreadsheet formulas. It can be integrated into your browser or utilized as a service with Node.js as your back-end technology.

## What HyperFormula can be used for?
Expand Down
87 changes: 87 additions & 0 deletions docs/.vuepress/components/HiringBanner.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<template>
<div class="hf-hiring-banner">
<span class="hf-hiring-banner__emoji" aria-hidden="true">🚀</span>
<span class="hf-hiring-banner__text">
Join HyperFormula team as a Senior Software Engineer
</span>
<a
class="hf-hiring-banner__cta"
href="https://handsontable.traffit.com/public/an/4b09e1395bf8ea42ef86db4c4657992c2f48673d"
target="_blank"
rel="noopener"
>
See the role and apply
</a>
</div>
</template>

<script>
export default {
name: 'HiringBanner',
};
</script>

<style scoped>
:global(:root) {
--hf-banner-height: 32px;
}

.hf-hiring-banner {
position: fixed;
bottom: 0;
left: 0;
right: 0;
z-index: 2000;
display: flex;
align-items: center;
gap: 0.75rem;
min-height: var(--hf-banner-height);
padding: 0.65rem 1rem;
background: linear-gradient(90deg, #0b63ce, #0f8fef);
color: #fff;
font-weight: 600;
font-size: 0.95rem;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}

.hf-hiring-banner__emoji {
font-size: 1.1rem;
}

.hf-hiring-banner__text {
flex: 1;
}

.hf-hiring-banner__cta {
color: #0b63ce;
background: #fff;
padding: 0.4rem 0.9rem;
border-radius: 999px;
font-weight: 700;
text-decoration: none;
transition: transform 0.1s ease, box-shadow 0.15s ease, background 0.15s ease;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.12);
}

.hf-hiring-banner__cta:hover,
.hf-hiring-banner__cta:focus {
background: #e8f3ff;
transform: translateY(-1px);
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.16);
}

:global(.theme-container) {
padding-bottom: calc(var(--hf-banner-height) + 1rem);
}

@media (max-width: 640px) {
.hf-hiring-banner {
flex-direction: column;
align-items: stretch;
}

.hf-hiring-banner__cta {
text-align: center;
}
}
</style>
3 changes: 2 additions & 1 deletion docs/.vuepress/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const searchPattern = new RegExp('^/api', 'i');
module.exports = {
title: 'HyperFormula (v' + HyperFormula.version + ')',
description: 'HyperFormula is an open-source, high-performance calculation engine for spreadsheets and web applications.',
globalUIComponents: ['HiringBanner'],
head: [
// Import HF (required for the examples)
[ 'script', { src: 'https://cdn.jsdelivr.net/npm/hyperformula/dist/hyperformula.full.min.js' } ],
Expand Down Expand Up @@ -40,7 +41,7 @@ module.exports = {
new Sentry.Replay({
maskAllText: false,
blockAllMedia: false,
}),
}),
],
});
};
Expand Down
2 changes: 2 additions & 0 deletions src/DependencyGraph/EmptyCellVertex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import {EmptyValue, EmptyValueType} from '../interpreter/InterpreterValue'
* Represents singleton vertex bound to all empty cells
*/
export class EmptyCellVertex {
public _graphId?: number

constructor() {}

/**
Expand Down
2 changes: 2 additions & 0 deletions src/DependencyGraph/FormulaVertex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import {Ast} from '../parser'
import {ColumnsSpan, RowsSpan} from '../Span'

export abstract class FormulaVertex {
public _graphId?: number

protected constructor(
protected formula: Ast,
protected cellAddress: SimpleCellAddress,
Expand Down
22 changes: 8 additions & 14 deletions src/DependencyGraph/Graph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export type DependencyQuery<Node> = (vertex: Node) => [(SimpleCellAddress | Simp
* Idea for performance improvement:
* - use Set<Node>[] instead of NodeId[][] for edgesSparseArray
*/
export class Graph<Node> {
export class Graph<Node extends { _graphId?: NodeId }> {
/**
* A sparse array. The value nodesSparseArray[n] exists if and only if node n is in the graph.
* @private
Expand All @@ -32,12 +32,6 @@ export class Graph<Node> {
*/
private edgesSparseArray: NodeId[][] = []

/**
* A mapping from node to its id. The value nodesIds.get(node) exists if and only if node is in the graph.
* @private
*/
private nodesIds: Map<Node, NodeId> = new Map()

/**
* A ProcessableValue object.
* @private
Expand Down Expand Up @@ -78,7 +72,7 @@ export class Graph<Node> {
* @param node - node to check
*/
public hasNode(node: Node): boolean {
return this.nodesIds.has(node)
return node._graphId !== undefined
}

/**
Expand Down Expand Up @@ -137,7 +131,7 @@ export class Graph<Node> {
* @param node - a node to be added
*/
public addNodeAndReturnId(node: Node): NodeId {
const idOfExistingNode = this.nodesIds.get(node)
const idOfExistingNode = node._graphId

if (idOfExistingNode !== undefined) {
return idOfExistingNode
Expand All @@ -148,7 +142,7 @@ export class Graph<Node> {

this.nodesSparseArray[newId] = node
this.edgesSparseArray[newId] = []
this.nodesIds.set(node, newId)
node._graphId = newId
return newId
}

Expand Down Expand Up @@ -183,7 +177,7 @@ export class Graph<Node> {
* Removes node from graph
*/
public removeNode(node: Node): [(SimpleCellAddress | SimpleCellRange), Node][] {
const id = this.getNodeId(node)
const id = node._graphId

if (id === undefined) {
throw this.missingNodeError(node)
Expand All @@ -199,7 +193,7 @@ export class Graph<Node> {
delete this.nodesSparseArray[id]
delete this.edgesSparseArray[id]
this.infiniteRangeIds.delete(id)
this.nodesIds.delete(node)
node._graphId = undefined

return dependencies
}
Expand Down Expand Up @@ -368,14 +362,14 @@ export class Graph<Node> {
* Returns the internal id of a node.
*/
public getNodeId(node: Node): NodeId | undefined {
return this.nodesIds.get(node)
return node._graphId
}

/**
*
*/
private getNodeIdIfNotNumber(node: Node | NodeId): NodeId | undefined {
return typeof node === 'number' ? node : this.nodesIds.get(node)
return typeof node === 'number' ? node : node._graphId
}

/**
Expand Down
2 changes: 2 additions & 0 deletions src/DependencyGraph/ParsingErrorVertex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import {ParsingError} from '../parser/Ast'
* Represents a cell that contains a parsing error.
*/
export class ParsingErrorVertex {
public _graphId?: number

/**
* Constructor
*/
Expand Down
1 change: 1 addition & 0 deletions src/DependencyGraph/RangeVertex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export type CriterionCache = Map<string, [any, CriterionLambda[]]>
* Represents vertex bound to range
*/
export class RangeVertex {
public _graphId?: number
public bruteForce: boolean
/** Cache for associative aggregate functions. */
private functionCache: Map<string, any>
Expand Down
2 changes: 2 additions & 0 deletions src/DependencyGraph/ValueCellVertex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ export interface RawAndParsedValue {
* Represents vertex which keeps static cell value
*/
export class ValueCellVertex {
public _graphId?: number

/** Static cell value. */
constructor(private parsedValue: ValueCellVertexValue, private rawValue: RawCellContent) {
}
Expand Down
Loading