
Research
Malicious npm Package Brand-Squats TanStack to Exfiltrate Environment Variables
A brand-squatted TanStack npm package used postinstall scripts to steal .env files and exfiltrate developer secrets to an attacker-controlled endpoint.
canvas-sheet
Advanced tools
A lightweight, high-performance spreadsheet component built on the HTML5 Canvas API for modern web applications. Unlike other canvas-based spreadsheet libraries, Canvas-Sheet uses a schema-based approach that gives you strong typing, validation, and custo
A lightweight, high-performance spreadsheet component built on the HTML5 Canvas API for modern web applications. Unlike other canvas-based spreadsheet libraries, Canvas-Sheet uses a schema-based approach that gives you strong typing, validation, and custom editors for each data type.
Demo: https://admin-remix.github.io/canvas-sheet
npm install canvas-sheet
<div id="spreadsheet-container" style="width: 100%; height: 500px;"></div>
import { Spreadsheet } from 'canvas-sheet';
// basic input and dropdown styles
import "canvas-sheet/dist/spreadsheet.css";
// Define the schema for your spreadsheet
const schema = {
id: { type: "number", decimal: false, label: "ID" },
name: {
type: "text",
required: true,
maxlength: 50,
label: "Full Name",
},
email: {
type: "email",
required: true,
label: "Email Address",
},
isActive: { type: "boolean", label: "Active" }
};
// Your data array
const data = [
{ id: 1, name: "John Doe", email: "john@example.com", isActive: true },
{ id: 2, name: "Jane Smith", email: "jane@example.com", isActive: false },
{ id: 3, name: "Bob Johnson", email: "bob@example.com", isActive: true }
];
// Initialize the spreadsheet
const spreadsheet = new Spreadsheet(
"spreadsheet-container", // Container ID
schema, // Column schema
data, // Initial data
{ // Optional configuration
headerHeight: 40,
defaultRowHeight: 36,
font: "14px Arial"
}
);
You can dynamically control which cells are disabled based on row data
const schema = {
// ... other fields
status: {
type: "select",
label: "Status",
values: [
{ id: 1, name: "Active" },
{ id: 2, name: "Pending" },
{ id: 3, name: "Inactive" }
],
// custom cell disabling logic
disabled: (rowData, rowIndex)=>{
console.log('Row index', rowIndex);
return rowData.isRestricted && rowData.locationId === 1
},
}
};
// Get current data
const currentData = spreadsheet.getData();
// Update the data
spreadsheet.setData(newData);
// Update a single cell
spreadsheet.updateCell(rowIndex, 'fieldName', newValue);
You can implement custom logic when cells are updated, including adding loading states and validation:
const spreadsheet = new Spreadsheet(
"spreadsheet-container",
schema,
data,
{
// ... other options
onCellsUpdate: (rows) => {
// Example: Show loading and then error state for email fields from a certain domain
for (const row of rows) {
if (row.columnKeys.includes('email') && row.data.email && row.data.email.endsWith('@sample.net')) {
// Set loading state on single cell
spreadsheet.updateCell(row.rowIndex, 'loading:email', true);
// Simulate async validation
setTimeout(() => {
// update multiple cells at once which is more efficient than updating one by one
spreadsheet?.updateCells([
{ rowIndex: row.rowIndex, colKey: 'loading:email', value: null },
{ rowIndex: row.rowIndex, colKey: 'error:email', value: `Account ${row.data.email} does not exist` }
]);
}, 2000);
}
}
},
onCellSelected: (rowIndex, colKey, rowData) => {
console.log('Selected', rowIndex, colKey, rowData[colKey]);
},
// ... other options
}
);
The native date picker is used by default. Enable the customDatePicker option which will trigger the onEditorOpen callback each time a date field is opened. When user selects a date from your custom date picker, call the setValueFromCustomEditor method to set the value of the cell which restores focus to the spreadsheet automatically.
let spreadsheet;
let selectedCellForEditor;
function openDatePicker(rowIndex: number, colKey: string, rowData: DataRow, bounds: CellBounds) {
selectedCellForEditor = { rowIndex, colKey, rowData, bounds };
const selectedDate = rowData[colKey];
const positionX = bounds.x;
const positionY = bounds.y;
const width = bounds.width;
const height = bounds.height;
console.log('open custom date picker', selectedDate, "at", {
positionX,
positionY,
width,
height
});
// or show a modal with a date picker
}
// call the "setValueFromCustomEditor" method to set the value of the cell after the date is selected
function closeDatePicker(value: string) {
spreadsheet?.setValueFromCustomEditor(selectedCellForEditor.rowIndex, selectedCellForEditor.colKey, value);
selectedCellForEditor = null;
}
spreadsheet = new Spreadsheet(
"spreadsheet-container",
schema,
data,
{
// ... other options
customDatePicker: true,
onEditorOpen: (rowIndex: number, colKey: string, rowData: DataRow, bounds: CellBounds) => {
openDatePicker(rowIndex, colKey, rowData, bounds);
},
// ... other options
}
);
Canvas-Sheet is highly customizable with many options:
const options = {
// Dimensions
defaultColumnWidth: 120,
defaultRowHeight: 30,
minColumnWidth: 50,
maxColumnWidth: 500,
minRowHeight: 25,
maxRowHeight: 100,
headerHeight: 36,
rowNumberWidth: 50,
// Styling
font: '14px Arial',
headerFont: 'bold 14px Arial',
textColor: '#333',
cellBgColor: '#ffffff',
activeCellBgColor: '#edf3ff',
selectedRowBgColor: '#f5f8ff',
selectedRangeBgColor: '#e8f0ff',
headerTextColor: '#333',
headerBgColor: '#f5f5f5',
gridLineColor: '#e0e0e0',
// Additional options
textAlign: 'left',
padding: 8,
verbose: false
// Custom date picker support
customDatePicker: false,
// when a date field is opened
onEditorOpen: (rowIndex, colKey, rowData, bounds) => void,
// when user presses delete on a column header, does not delete the column
// you have to call "removeColumnByIndex()" to delete the column
onColumnDelete: (colIndex, schema) => void,
// after rows are deleted
onRowDeleted: (rows) => void,
// when a cell is selected
onCellSelected: (rowIndex, colKey, rowData) => void,
// when cells are updated
onCellsUpdate: (rows) => void,
};
Each column can have type-specific configuration:
const schema = {
// Text field with validation
name: {
type: "text",
required: true,
maxlength: 50,
label: "Name"
},
// Number field with options
amount: {
type: "number",
decimal: true, // Allow decimal values
label: "Amount"
},
// Boolean field (checkbox)
isActive: {
type: "boolean",
label: "Active"
},
// Date field
createdAt: {
type: "date",
label: "Created Date"
},
// Select/dropdown field
status: {
type: "select",
label: "Status",
values: [
{ id: 1, name: "Active" },
{ id: 2, name: "Pending" },
{ id: 3, name: "Inactive" }
],
disabled: (data)=>{
return data.status === 3 && !data.isActive;
}
}
};
Canvas-Sheet handles many events automatically:
Canvas-Sheet works in all modern browsers that support HTML5 Canvas.
MIT
FAQs
A lightweight, high-performance spreadsheet component built on the HTML5 Canvas API for modern web applications. Unlike other canvas-based spreadsheet libraries, Canvas-Sheet uses a schema-based approach that gives you strong typing, validation, and custo
We found that canvas-sheet demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Research
A brand-squatted TanStack npm package used postinstall scripts to steal .env files and exfiltrate developer secrets to an attacker-controlled endpoint.

Research
Compromised SAP CAP npm packages download and execute unverified binaries, creating urgent supply chain risk for affected developers and CI/CD environments.

Company News
Socket has acquired Secure Annex to expand extension security across browsers, IDEs, and AI tools.