@richpods/tiny-spreadsheet-editor
v0.1.0
Published
A standalone Vue 3 component for editing 2-dimensional data arrays
Maintainers
Readme
RichPods Tiny Spreadsheet Editor
A standalone Vue 3 component for editing 2-dimensional data arrays with a spreadsheet-like interface. Written in Typescript, ESM-only support.
Features
- Edit two-dimensional arrays in a simple spreadsheet-like UI
- Support for string and number data types
- Configurable row and column headers
- Locale-aware number formatting (respects browser locale)
- Resizable columns with drag-and-drop
- Custom cell validation with visual error states
- Metadata storage for column widths
- Fully customizable via CSS variables
Installation
npm i @richpods/tiny-spreadsheet-editorBasic Usage
<template>
<TinySpreadsheetEditor
v-model="data"
:headers="Headers.BOTH"
:cell-data="CellData.NUMBER" />
</template>
<script setup lang="ts">
import { ref } from "vue";
import { TinySpreadsheetEditor, Headers, CellData } from "@richpods/tiny-spreadsheet-editor";
import "@richpods/tiny-spreadsheet-editor/styles";
const data = ref([
["", "A", "B", "C"],
["1", 10, 20, 30],
["2", 40, 50, 60],
]);
</script>Props
| Prop | Type | Default | Description |
| ------------------ | --------------- | ----------------- |--------------------------------------------------------------------------------|
| modelValue | GridData | [['']] | 2D array of cell values (v-model) |
| headers | HeadersType | 'none' | Header configuration |
| cellData | CellDataType | 'string' | Data type for cells |
| localeFormatting | boolean | true | Enable locale-aware number formatting using the locale provided by navigator |
| locale | string | undefined | Overrides locale, e.g if navigator is unavailable (SSR) |
| validator | CellValidator | undefined | Custom validation function |
| meta | GridMeta | { columns: [] } | Column metadata (v-model:meta) |
Headers
import { Headers } from "@richpods/tiny-spreadsheet-editor";
Headers.NONE; // No headers
Headers.ROW; // First row is a header
Headers.COLUMN; // First column is a header
Headers.BOTH; // First row and column are headersCell Data Types
import { CellData } from "@richpods/tiny-spreadsheet-editor";
CellData.STRING; // All cells contain strings
CellData.NUMBER; // All cells contain numbersEvents
| Event | Payload | Description |
| ------------------- | ---------- | --------------------------------------------- |
| update:modelValue | GridData | Emitted when data changes |
| update:meta | GridMeta | Emitted when metadata changes (column widths) |
Exposed Methods
Access methods via template ref:
<template>
<TinySpreadsheetEditor ref="editorRef" v-model="data" />
<button @click="editorRef?.addRow()">Add Row</button>
</template>
<script setup lang="ts">
import { ref } from "vue";
import { TinySpreadsheetEditor } from "@richpods/tiny-spreadsheet-editor";
const editorRef = ref<InstanceType<typeof TinySpreadsheetEditor> | null>(null);
</script>| Method | Signature | Description |
| -------------- | -------------------------- | ---------------------------- |
| addRow | (index?: number) => void | Add row at index (or end) |
| removeRow | (index: number) => void | Remove row at index |
| addColumn | (index?: number) => void | Add column at index (or end) |
| removeColumn | (index: number) => void | Remove column at index |
| getData | () => GridData | Get copy of current data |
| getMeta | () => GridMeta | Get copy of current metadata |
| setMeta | (meta: GridMeta) => void | Set metadata |
Validation
Provide a validator function to validate cell values:
import type { CellValidator } from "@richpods/tiny-spreadsheet-editor";
const validator: CellValidator = (value, row, col) => {
if (typeof value === "number" && value < 0) {
return { valid: false, message: "Value must be positive" };
}
return { valid: true };
};<TinySpreadsheetEditor v-model="data" :validator="validator" />Invalid cells display a red border and show the error message on focus.
Column Resizing & Metadata
Columns can be resized by dragging the column borders. Column widths are stored in metadata which can be saved and restored:
<template>
<TinySpreadsheetEditor v-model="data" v-model:meta="meta" />
</template>
<script setup lang="ts">
import { ref, watch } from "vue";
import type { GridMeta } from "@richpods/tiny-spreadsheet-editor";
const meta = ref<GridMeta>({ columns: [] });
// Save to localStorage
watch(
meta,
(newMeta) => {
localStorage.setItem("csv-meta", JSON.stringify(newMeta));
},
{ deep: true }
);
// Restore from localStorage
const saved = localStorage.getItem("csv-meta");
if (saved) {
meta.value = JSON.parse(saved);
}
</script>Styling
The component uses CSS variables for theming. Import the default styles:
import "@richpods/tiny-spreadsheet-editor/styles";CSS Variables
Override these variables to customize the appearance:
:root {
--tse-border-color: #d0d0d0;
--tse-border-width: 1px;
--tse-header-bg: #f5f5f5;
--tse-header-text: #333;
--tse-header-font-weight: 600;
--tse-cell-bg: #fff;
--tse-cell-text: #333;
--tse-cell-padding: 4px 8px;
--tse-cell-min-width: 80px;
--tse-cell-focus-border: #0066cc;
--tse-cell-focus-shadow: 0 0 0 2px rgba(0, 102, 204, 0.2);
--tse-cell-invalid-bg: #ffe6e6;
--tse-cell-invalid-border: #cc0000;
--tse-font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
--tse-font-size: 14px;
--tse-line-height: 1.4;
}Custom Theme
Create a custom theme by overriding the variables:
.dark-theme .tiny-spreadsheet-editor {
--tse-border-color: #444;
--tse-header-bg: #2a2a2a;
--tse-header-text: #eee;
--tse-cell-bg: #1a1a1a;
--tse-cell-text: #eee;
}TypeScript
All types are exported:
import type {
GridData, // CellValue[][]
CellValue, // string | number
HeadersType, // 'row' | 'column' | 'both' | 'none'
CellDataType, // 'string' | 'number'
GridMeta, // { columns: ColumnMeta[] }
ColumnMeta, // { width?: number }
CellValidator, // (value, row, col) => ValidationResult
ValidationResult, // { valid: boolean, message?: string }
} from "@richpods/tiny-spreadsheet-editor";Development
# Install dependencies
npm install
# Start dev server with demo
npm run dev
# Type check
npm run typecheck
# Format code
npm run format
# Build library
npm run build
# Build demo
npm run build:demoLicense
Licensed under the Blue Oak Model License 1.0.0. See LICENSE.md.
Included Fonts
The demo bundles the Playfair Display typeface (used for headings). The font file is licensed under the SIL Open Font License (OFL 1.1).
