A React + Tailwind + @mieweb/ui UI built on top of the DataVis ACE data acquisition and computation engine.
Live Demo · Storybook · Rewrite Plan
DataVis is a set of tools for exploring, manipulating, and visualizing data. It features, among other things:
- Extensible data acquisition methods; it has builtin support for HTTP (XML, JSON, CSV), local CSV, in-page HTML table & JS value.
- Intelligent automatic handling of numerous data types such as dates, currency, and embedded JSON. User-added type handling is available.
- Efficient and extensive type-associated filtering, grouping, pivotting, and aggregation operations.
- Flexible user interfaces for exploring and understanding data, including tables and graphs. This project is one such interface.
graph LR
subgraph ACE["DataVis ACE (from wcdatavis project)"]
Source
View
Aggregates
Types
Prefs
end
subgraph Adapter["Adapter Layer"]
EventBridge["EventBridge"]
Hooks["useSource · useView · usePrefs"]
TypeAdapter["Type formatting"]
end
subgraph ReactUI["React UI"]
DataGrid["DataGrid"]
Filters["FilterBar"]
Controls["ControlPanel"]
Tables["Table Renderers"]
Dialogs["Dialogs"]
end
ACE --> Adapter --> ReactUI
classDef core fill:#e8f5e9,stroke:#2e7d32
classDef adapter fill:#fff3e0,stroke:#e65100
classDef ui fill:#e3f2fd,stroke:#1565c0
class Source,View,Aggregates,Types,Prefs core
class EventBridge,Hooks,TypeAdapter adapter
class DataGrid,Filters,Controls,Tables,Dialogs ui
| Layer | Technology |
|---|---|
| Framework | React 19 |
| UI Components | @mieweb/ui (^0.2.2) |
| Styling | Tailwind CSS 4 (@tailwindcss/vite) |
| Build | Vite 6 (library mode, ES module output) |
| Language | TypeScript 5.8 (strict) |
| Drag & Drop | @dnd-kit/core + @dnd-kit/sortable |
| Linting | ESLint 10 + typescript-eslint + jsx-a11y |
| Storybook | Storybook 8.6 (React + Vite) |
| i18n | TSV-based, 10 locales |
# Install dependencies
npm install
# Start dev server (http://localhost:5173)
npm run dev
# Start Storybook (http://localhost:6006)
npm run storybook
# Build library
npm run build| Script | Description |
|---|---|
dev |
Start Vite dev server |
build |
Build library (ES module output to dist/) |
preview |
Preview production build locally |
storybook |
Start Storybook on port 6006 |
build-storybook |
Build static Storybook site |
lint |
Run ESLint + i18n key check |
lint:eslint |
Run ESLint on src/ |
lint:i18n |
Verify all t()/trans() keys exist in en-US.tsv |
lint:i18n:update |
Auto-append missing i18n keys to TSV |
src/
├── index.ts # Library entry point (exports adapters + components)
├── main.tsx # Demo app (3-tab example with mock data)
├── adapters/ # Bridges wcdatavis core → React
│ ├── event-bridge.ts # mixinEventHandling → React hooks
│ ├── use-data.ts # useSource(), useView() hooks
│ ├── use-prefs.ts # usePrefs() hook for perspectives
│ ├── type-adapter.ts # Cell formatting + comparison
│ ├── group-adapter.ts # Group function registry bridge
│ └── colconfig-adapter.ts # Column config format bridge
├── components/
│ ├── DataGrid.tsx # Top-level grid component
│ ├── TitleBar.tsx # Header with title + action buttons
│ ├── GridToolbar.tsx # Mode-aware composite toolbar
│ ├── DetailSlider.tsx # Row detail slide-in panel
│ ├── OperationsPalette.tsx # Row-level action buttons
│ ├── LoadingOverlay.tsx # Spinner overlay
│ ├── LanguageSelector.tsx # Locale picker (10 locales)
│ ├── controls/ # Group / Pivot / Aggregate control panel
│ ├── dialogs/ # Modal dialogs (6 total)
│ ├── filters/ # Per-column filter widgets
│ ├── table/ # Table renderers (plain, grouped, pivot)
│ └── toolbars/ # Plain, Group, Pivot, Prefs toolbars
├── demo/ # Demo data + filter engine
├── i18n/ # TransContext, useTranslation(), locale TSVs
└── scripts/ # i18n lint checker
| Component | Description |
|---|---|
DataGrid |
Top-level component composing title bar, toolbar, control panel, filter bar, table, and dialogs |
TitleBar |
Grid header with title, row count badge, and action buttons (debug, export, refresh, controls toggle) |
GridToolbar |
Shows the right toolbar based on data mode — plain, grouped, or pivot |
GlobalSearchControl |
Plain-mode visual search across visible columns with highlighting and an accessible result count |
DetailSlider |
Slide-in panel from the right edge for row detail content |
OperationsPalette |
Inline toolbar of icon buttons for row-level operations |
LoadingOverlay |
Block-UI overlay with @mieweb/ui Spinner |
LanguageSelector |
Locale picker supporting 10 languages |
| Component | Description |
|---|---|
ControlPanel |
Four-section panel: Filter, Group, Pivot, Aggregate. Uses @dnd-kit for drag-and-drop field reordering |
ControlSection |
Single section with dropdown to add fields and a sortable field list |
AggregateSection |
Aggregate function selector with multi-field argument support |
FieldPill |
Draggable field chip with optional group-function button |
| Component | Description |
|---|---|
FilterBar |
Composite filter bar — one filter widget per visible column |
StringFilter |
Text/dropdown filter with contains, equals, in, blank operators |
NumberFilter |
Numeric filter with comparison operators |
DateFilter |
Date filter with on/before/after/between/every/current/last operators |
BooleanFilter |
Checkbox filter for boolean columns |
FilterContext |
React context for dynamic filter management (add/remove from column headers) |
The plain grid toolbar includes a debounced global search that visually narrows the rows already returned by ACE. It searches formatted display text across visible columns before NITRO's row display limit, so matches beyond the first rendered batch are included. Existing per-column ACE filters run first, and global search narrows their result without changing the ACE filter specification or rerunning grouping, pivoting, aggregation, sorting, or data acquisition.
The feature is intentionally visual-only and plain-mode-only. It is hidden and cleared in grouped or pivoted output, and it does not change ACE counts, aggregates, CSV exports, clipboard exports, preferences, or other consumers of the underlying view. Custom cell renderers can provide exact searchable display text through TableColumn.getSearchText.
| Component | Description |
|---|---|
TableRenderer |
Selects sub-renderer based on data mode (plain / group-detail / group-summary / pivot) |
PlainTable |
Flat table with column resize, reorder, sticky headers, context menu, sort, keyboard nav, row selection, zebra striping |
GroupDetailTable |
Grouped view with collapsible headers, per-group aggregates, two-row sub-column headers |
GroupSummaryTable |
One-row-per-group summary with aggregate values |
PivotTable |
Pivot matrix of row-values × column-values with aggregate cells |
HeaderContextMenu |
Right-click context menu for column headers |
| Component | Description |
|---|---|
ColumnConfigDialog |
Column order, visibility, display names, per-column flags with drag-and-drop reordering |
TemplateEditorDialog |
Edit Handlebars/Squirrelly templates for plain, grouped, and pivot modes |
DebugDialog |
Live debug info in 4 tabs (Source, View, Grid, Prefs) with collapsible JSON sections |
GridTableOptionsDialog |
Cell display-format template customization |
GroupFunctionDialog |
Group function selector with categorized buttons |
PerspectiveManagerDialog |
Perspective CRUD — JSON inspector, create/rename/delete |
| Component | Description |
|---|---|
PlainToolbar |
Auto show-more, show all, columns, templates, row mode, auto resize |
GroupToolbar |
Group mode toggle, total row, expand all, pin groups |
PivotToolbar |
Show totals, pin groups, hide zero values, templates |
PrefsToolbar |
Perspective reset, back/forward, perspective dropdown, save/rename/delete |
The adapter layer bridges the legacy wcdatavis data-processing objects into React without modifying the original code.
| Module | Purpose |
|---|---|
EventBridge |
Converts mixinEventHandling pub/sub (.on/.off/.fire) to React hooks: useDataVisEvent(), useDataVisEvents() |
useSource() |
Wraps Source construction and data fetching with reactive state |
useView() |
Wraps ComputedView lifecycle — sorting, filtering, grouping, pivoting, aggregation |
usePrefs() |
Wraps Prefs for perspective management (reset, history, CRUD) |
formatCellValue() |
Bridges types.js formatters to React-safe output |
compareValues() |
Type-aware comparison for client-side sorting |
adaptGroupFunctionRegistry() |
Converts legacy group function registry to React GroupFunction[] |
ordMapToColumnConfigs() |
Converts OrdMap<FieldColConfig> to React ColumnConfig[] |
- No jQuery in new UI code — all DOM is React-owned
- Event bridge — legacy event system maps to React callbacks and hooks
- Data hooks —
useSource()anduseView()expose reactive state - Type formatting — reuses
types.jsformatters, converting HTML output to React elements where needed - i18n — reuses
trans()directly viaTransContext
Translations are stored as tab-separated key/value pairs in TSV files. The English source of truth is src/i18n/en-US.tsv.
Supported locales: en-US, es-MX, fr-FR, id-ID, nl-NL, pt-BR, ru-RU, th-TH, vi-VN, zh-Hans-CN
Components use the useTranslation() hook which provides a t(key, ...args) function. The default returns '' for unknown keys so the t(KEY) || 'Fallback' pattern works throughout.
# Check all i18n keys are defined
npm run lint:i18n
# Auto-append missing keys with fallback text
npm run lint:i18n:updateThe i18n lint script (scripts/check-i18n-keys.mjs) uses the TypeScript compiler API to walk src/ and extract all t()/trans() calls, then compares against keys defined in the TSV.
The grid supports 8 aggregate functions that compute per-group values in grouped table mode:
| Function | Label | Description |
|---|---|---|
sum |
Sum | Numeric sum |
avg |
Average | Arithmetic mean |
count |
Count | Row count (no field required) |
counta |
Count Values | Count of non-null/non-empty values |
countu |
Count Unique | Count of distinct non-null/non-empty values |
min |
Min | Minimum value |
max |
Max | Maximum value |
list |
Unique Values | Comma-separated unique non-empty values |
Aggregate values appear in group header rows, aligned under matching column headers with a two-row sub-column layout (e.g., Salary → SUM | AVG).
The demo (src/main.tsx) provides three tabbed example grids with mock data:
| Tab | Rows | Columns | Description |
|---|---|---|---|
| Simple | 8 | 8 | Employee directory |
| Wide (50 columns) | 20 | 50 | Contact + appointment + location data |
| Large (5K rows) | 5,000 | 33 | Financial ledger + inventory |
Start the demo with npm run dev and open http://localhost:5173.
Interactive component catalog with stories for all major components:
| Story | Components |
|---|---|
| DataGrid | Full grid with mock data |
| ControlPanel | Group/Pivot/Aggregate controls |
| DetailSlider | Row detail slide-in panel |
| Dialogs | All 6 dialog components |
| FilterBar | Filter widgets for all column types |
| TableRenderer | Plain, grouped, and pivot table modes |
Start Storybook with npm run storybook and open http://localhost:6006.
The rewrite follows a phased approach documented in v0-rewrite-plan.md.
| Phase | Description | Status |
|---|---|---|
| 0 | Scaffold & adapter layer | ✅ Complete |
| 1 | Grid shell (DataGrid, Toolbar, Slider, LoadingOverlay) | ✅ Complete |
| 2 | Filters & Control Panel | ✅ Complete |
| 3 | Dialog components (6 dialogs) | ✅ Complete |
| 4 | Table renderer (plain, grouped, pivot) | ✅ Complete |
| 5 | Graph shell (Chart.js wrapper) | Planned |
| 6 | Polish & integration | In progress |
Additional features landed post-Phase 4: sorting end-to-end, client-side filtering, i18n lint guard, accessibility audit (ARIA labels, keyboard nav, focus management), group-by with aggregates.
MIT — Copyright (c) 2025 Medical Informatics Engineering, Inc.