diff --git a/.github/workflows/ci-solid.yml b/.github/workflows/ci-solid.yml new file mode 100644 index 0000000..475842f --- /dev/null +++ b/.github/workflows/ci-solid.yml @@ -0,0 +1,57 @@ +name: CI Solid + +on: + pull_request: + paths: + - '.github/workflows/ci-solid.yml' + - '.github/workflows/publish-solid.yml' + - 'projects/ui-grid-solid/**' + - 'projects/ui-grid-core/**' + - 'projects/ui-grid-vanilla/**' + - 'scripts/build-solid.mjs' + - 'scripts/sync-versions.mjs' + - 'package.json' + - 'package-lock.json' + push: + branches: + - main + paths: + - '.github/workflows/ci-solid.yml' + - '.github/workflows/publish-solid.yml' + - 'projects/ui-grid-solid/**' + - 'projects/ui-grid-core/**' + - 'projects/ui-grid-vanilla/**' + - 'scripts/build-solid.mjs' + - 'scripts/sync-versions.mjs' + - 'package.json' + - 'package-lock.json' + workflow_dispatch: + +jobs: + build-and-test: + runs-on: ubuntu-latest + + steps: + - name: Checkout source + uses: actions/checkout@v7 + + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version: 26.4.0 + cache: npm + cache-dependency-path: | + package-lock.json + projects/ui-grid-solid/package-lock.json + + - name: Install repository dependencies + run: npm ci --legacy-peer-deps + + - name: Install Solid adapter dependencies + run: npm ci --prefix projects/ui-grid-solid + + - name: Build Solid adapter and internal dependencies + run: npm run build:solid + + - name: Run Solid integration, SSR, hydration, package, and example checks + run: npm run test:solid diff --git a/.github/workflows/publish-solid.yml b/.github/workflows/publish-solid.yml new file mode 100644 index 0000000..32f9bde --- /dev/null +++ b/.github/workflows/publish-solid.yml @@ -0,0 +1,76 @@ +name: Publish Solid package + +on: + workflow_dispatch: + inputs: + ref: + description: Branch, tag, or commit to publish + required: true + default: main + version: + description: Public version to publish + required: true + default: 0.1.0 + +jobs: + publish: + runs-on: ubuntu-latest + + steps: + - name: Checkout source + uses: actions/checkout@v7 + with: + ref: ${{ inputs.ref }} + + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version: 26.4.0 + registry-url: https://registry.npmjs.org + scope: '@ornery' + always-auth: true + cache: npm + cache-dependency-path: | + package-lock.json + projects/ui-grid-solid/package-lock.json + + - name: Install repository dependencies + run: npm ci --legacy-peer-deps + + - name: Install Solid adapter dependencies + run: npm ci --prefix projects/ui-grid-solid + + - name: Verify internal packages are published + run: | + VERSION="${{ inputs.version }}" + npm view "@ornery/ui-grid-core@$VERSION" version + npm view "@ornery/ui-grid-vanilla@$VERSION" version + + - name: Build and test the Solid package + run: | + npm run build:solid + npm run test:solid + + - name: Set and verify the publish manifest + env: + VERSION: ${{ inputs.version }} + run: | + node - <<'NODE' + const fs = require('node:fs'); + const path = './projects/ui-grid-solid/package.json'; + const pkg = require(path); + pkg.version = process.env.VERSION; + for (const name of ['@ornery/ui-grid-core', '@ornery/ui-grid-vanilla']) { + pkg.peerDependencies[name] = process.env.VERSION; + } + fs.writeFileSync(path, `${JSON.stringify(pkg, null, 2)}\n`); + if (pkg.version !== process.env.VERSION) throw new Error('Package version was not updated'); + for (const name of ['@ornery/ui-grid-core', '@ornery/ui-grid-vanilla']) { + if (pkg.peerDependencies[name] !== process.env.VERSION) throw new Error(`Invalid ${name} peer`); + } + NODE + + - name: Publish to npm + run: npm publish ./projects/ui-grid-solid --access public --registry=https://registry.npmjs.org + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/README.md b/README.md index 2195c01..5486d45 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ # UI Grid — Remastered -**The modern multi-platform data grid. Every feature free and open source. Built for Angular, Web-Components, React, native Rust/egui, and native C/LVGL.** +**The modern multi-platform data grid. Every feature free and open source. Built for Angular, Web Components, React, SolidJS, native Rust/egui, and native C/LVGL.** A from-scratch rewrite of the original [AngularJS ui-grid](https://github.com/angular-ui/ui-grid) — by the original author. Same `gridOptions` / `columnDefs` / `onRegisterApi` api surface, modern Angular signals internals, and zero legacy baggage. @@ -24,7 +24,7 @@ It proves that the datagrid cabal wants you to think it's hard to write a data g - **Original authorship** — built by the same engineer who created AngularJS ui-grid, with a decade of hindsight on what worked and what didn't - **Familiar API** — if you used the original ui-grid, you already know this one: `gridOptions`, `columnDefs`, `onRegisterApi`, `gridApi.core.*` -- **Modern internals** — shared vanilla web component core with Shadow DOM encapsulation; Angular and React wrappers are thin bridges that project framework templates into the vanilla element via slot-based portals +- **Modern internals** — shared vanilla web component core with Shadow DOM encapsulation; Angular, React, and SolidJS wrappers are thin bridges that project framework templates into the vanilla element via slot-based portals - **No legacy** — no `$scope`, no Bower, no Grunt, no jQuery, no module system from 2013 --- @@ -62,6 +62,7 @@ Everything below ships free and MIT-licensed. No enterprise tier, no license key | **SSR Support** | **Free** | — | ~$999/dev/yr | — | — | — | | i18n (6 locales built-in) | **Free** | Free | — | Free | Paid | Community\* | | React | **Yes** | Wrapper | Wrapper | No | Wrapper | Wrapper | +| SolidJS | **Yes** | Wrapper | Wrapper | No | Wrapper | Wrapper | | Rust/egui Native | **Yes** | No | No | No | No | No | | C/LVGL Native | **Yes** | No | No | No | No | No | | Angular | **Yes** | Wrapper | Wrapper | No | Wrapper | Wrapper | @@ -138,9 +139,76 @@ function MyGrid() { } ``` +### SolidJS + +The SolidJS adapter mounts the same vanilla Web Component while preserving +SolidJS ownership, reactive props, local renderer state, and lifecycle cleanup. +It supports browser rendering, SSR and hydration, custom JSX cells and headers, +expandable detail rows, and JSX cell editors. + +```bash +npm install @ornery/ui-grid-solid @ornery/ui-grid-core @ornery/ui-grid-vanilla solid-js @solidjs/web +``` + +Configure the SolidJS JSX runtime with `jsxImportSource: "@solidjs/web"`, then +pass reactive options to `UiGrid`: + +```tsx +/** @jsxImportSource @solidjs/web */ +import { createSignal } from 'solid-js'; +import { UiGrid, type GridOptions, type UiGridCellRenderers } from '@ornery/ui-grid-solid'; + +const cellRenderers: UiGridCellRenderers = { + name: (context) => {String(context.value)}, +}; + +export function PeopleGrid() { + const [rows, setRows] = createSignal([ + { id: 1, name: 'Alice', role: 'Engineer' }, + { id: 2, name: 'Bob', role: 'Designer' }, + ]); + + const options = (): GridOptions => ({ + id: 'people-grid', + data: rows(), + columnDefs: [{ name: 'name' }, { name: 'role' }], + rowIdentity: (row) => String(row['id']), + enableSorting: true, + enableFiltering: true, + enableVirtualization: true, + }); + + return ( +
+ + +
+ ); +} +``` + +`options` uses the shared `GridOptions` contract. The adapter updates the same +grid element when signals change, so interactive sorting, filters, column order, +and other controller state remain intact. `onRegisterApi` exposes the shared +`UiGridApi`; all projected SolidJS roots and subscriptions are disposed when the +component unmounts. + +See the [SolidJS adapter guide](projects/ui-grid-solid/README.md), the +[implementation notes](), and the +[SolidStart example](projects/ui-grid-solid/examples/solid-start/README.md). + ### Web Components (Vanilla) -The grid's rendering engine is a framework-free custom element (``) built on `@ornery/ui-grid-core` with pure DOM rendering and Shadow DOM encapsulation. Both the Angular and React wrappers are thin bridges around this same element — they mount ``, pass options, and project framework-specific templates into it via a slot-based portal system. +The grid's rendering engine is a framework-free custom element (``) built on `@ornery/ui-grid-core` with pure DOM rendering and Shadow DOM encapsulation. The Angular, React, and SolidJS wrappers are thin bridges around this same element — they mount ``, pass options, and project framework-specific templates into it via a slot-based portal system. ```bash npm install @ornery/ui-grid-vanilla @ornery/ui-grid-core @@ -282,9 +350,9 @@ The LVGL demo currently exercises the native C grid shell with sorting, grouping - **Save/Restore State** — serialize and restore sort, filters, grouping, collapsed groups, pinning, column order, column widths, pagination, selection, focused cell, tree/expandable expansion, and scroll position (per-field opt-in flags) - **Native Rust and C Grids** — shared Rust core with native Rust/egui and native C/LVGL adapters driven by the same projection and command contract - **Auto Resize** — ResizeObserver-driven viewport height recalculation -- **Custom Cell Templates** — Angular `ng-template`, React `cellRenderers` map (per-column render functions), vanilla `