From dc886c164008d5ada26f8d27c9acebac6d745e0a Mon Sep 17 00:00:00 2001
From: peter <20213436+petera2c@users.noreply.github.com>
Date: Sun, 31 May 2026 09:56:25 -0500
Subject: [PATCH 1/4] Hover fix
---
.github/workflows/publish.yml | 5 +-
.github/workflows/test.yml | 34 ++
.../core/src/core/rendering/TableRenderer.ts | 4 +-
packages/react/package.json | 5 +-
.../react/src/__tests__/rowHover.test.tsx | 112 +++++
packages/react/vitest.config.ts | 20 +
packages/react/vitest.setup.ts | 37 ++
pnpm-lock.yaml | 446 +++++++++++++++++-
8 files changed, 652 insertions(+), 11 deletions(-)
create mode 100644 .github/workflows/test.yml
create mode 100644 packages/react/src/__tests__/rowHover.test.tsx
create mode 100644 packages/react/vitest.config.ts
create mode 100644 packages/react/vitest.setup.ts
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index ee1d66951..f6d0370f1 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -12,6 +12,9 @@ permissions:
contents: write
jobs:
+ tests:
+ uses: ./.github/workflows/test.yml
+
verify-examples:
runs-on: ubuntu-latest
steps:
@@ -37,7 +40,7 @@ jobs:
run: pnpm run build:examples
publish:
- needs: verify-examples
+ needs: [tests, verify-examples]
runs-on: ubuntu-latest
steps:
- name: Checkout code
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
new file mode 100644
index 000000000..e838727e5
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -0,0 +1,34 @@
+name: Test
+
+on:
+ pull_request:
+ push:
+ branches:
+ - main
+ - master
+ # Allow other workflows (e.g. publish) to gate on this suite.
+ workflow_call:
+
+jobs:
+ test:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Setup pnpm
+ uses: pnpm/action-setup@v4
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: "22"
+ cache: "pnpm"
+
+ - name: Install dependencies
+ run: pnpm install --frozen-lockfile
+
+ # The React adapter Vitest suite aliases `simple-table-core` to the core
+ # *source*, so no build step is required.
+ - name: Run React adapter tests
+ run: pnpm --filter @simple-table/react run test
diff --git a/packages/core/src/core/rendering/TableRenderer.ts b/packages/core/src/core/rendering/TableRenderer.ts
index 4f0cd5d95..43654af89 100644
--- a/packages/core/src/core/rendering/TableRenderer.ts
+++ b/packages/core/src/core/rendering/TableRenderer.ts
@@ -455,7 +455,9 @@ export class TableRenderer {
selectedRowCount,
cellUpdateFlash: deps.config.cellUpdateFlash,
useOddColumnBackground: deps.config.useOddColumnBackground,
- useHoverRowBackground: deps.config.useHoverRowBackground,
+ // Defaults to true (documented default) so row hover works out of the box
+ // when consumers don't explicitly pass the flag. Explicit `false` is honored.
+ useHoverRowBackground: deps.config.useHoverRowBackground ?? true,
useOddEvenRowBackground: deps.config.useOddEvenRowBackground,
rowGrouping: deps.config.rowGrouping,
headers: deps.effectiveHeaders,
diff --git a/packages/react/package.json b/packages/react/package.json
index 5519c059b..6a7a47bc8 100644
--- a/packages/react/package.json
+++ b/packages/react/package.json
@@ -7,6 +7,7 @@
"scripts": {
"build": "rollup -c",
"preview": "rollup -c -w",
+ "test": "vitest run",
"version:patch": "npm version patch && git push && git push --tags",
"version:minor": "npm version minor && git push && git push --tags",
"version:major": "npm version major && git push && git push --tags"
@@ -43,6 +44,7 @@
"@rollup/plugin-node-resolve": "^15.3.0",
"@types/react": "^18.0.0",
"@types/react-dom": "^18.0.0",
+ "jsdom": "^24.1.3",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"rollup": "^2.79.2",
@@ -51,7 +53,8 @@
"rollup-plugin-terser": "^7.0.2",
"rollup-plugin-typescript2": "^0.37.0",
"tslib": "^2.8.1",
- "typescript": "^4.9.5"
+ "typescript": "^4.9.5",
+ "vitest": "^2.1.9"
},
"description": "React adapter for simple-table-core — use the Simple Table data grid with full React component support for renderers.",
"repository": {
diff --git a/packages/react/src/__tests__/rowHover.test.tsx b/packages/react/src/__tests__/rowHover.test.tsx
new file mode 100644
index 000000000..b12b34862
--- /dev/null
+++ b/packages/react/src/__tests__/rowHover.test.tsx
@@ -0,0 +1,112 @@
+import { createElement } from "react";
+import { createRoot, type Root } from "react-dom/client";
+import { afterEach, describe, expect, it } from "vitest";
+import { SimpleTable } from "../index";
+import type { ReactHeaderObject, SimpleTableReactProps } from "../index";
+
+const headers: ReactHeaderObject[] = [
+ { accessor: "id", label: "ID", width: 80, type: "number" },
+ { accessor: "name", label: "Name", width: 120, type: "string" },
+];
+
+const rows = [
+ { id: 1, name: "Alice" },
+ { id: 2, name: "Bob" },
+];
+
+let container: HTMLDivElement | null = null;
+let root: Root | null = null;
+
+afterEach(() => {
+ root?.unmount();
+ root = null;
+ container?.remove();
+ container = null;
+});
+
+const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
+
+/** Poll the DOM until `selector` matches, or fail after `timeoutMs`. */
+async function waitForElement(
+ scope: HTMLElement,
+ selector: string,
+ timeoutMs = 3000,
+): Promise {
+ const start = Date.now();
+ while (Date.now() - start < timeoutMs) {
+ const el = scope.querySelector(selector);
+ if (el) return el;
+ await wait(20);
+ }
+ throw new Error(`Timed out waiting for element: ${selector}`);
+}
+
+/** Mount the React adapter and wait until the first body cell has rendered. */
+async function mountTable(
+ props: Partial,
+): Promise<{ host: HTMLDivElement; firstCell: HTMLElement }> {
+ const host = document.createElement("div");
+ document.body.appendChild(host);
+ container = host;
+
+ root = createRoot(host);
+ root.render(
+ createElement(SimpleTable, {
+ defaultHeaders: headers,
+ rows,
+ getRowId: (p) => String((p.row as { id?: number })?.id),
+ height: "250px",
+ theme: "light",
+ ...props,
+ }),
+ );
+
+ // The adapter mounts the vanilla table asynchronously (queueMicrotask + rAF),
+ // so wait until a body cell has rendered.
+ let firstCell: HTMLElement;
+ try {
+ firstCell = await waitForElement(host, ".st-body-container .st-cell");
+ } catch (e) {
+ // eslint-disable-next-line no-console
+ console.log("DEBUG innerHTML:\n", host.innerHTML.slice(0, 4000));
+ throw e;
+ }
+ return { host, firstCell };
+}
+
+// "st-row-hovered" is the class the core toggles on every cell in a hovered
+// row; the CSS rule `.st-cell.st-row-hovered { background-color:
+// var(--st-hover-row-background-color) }` is what actually changes the color.
+// Asserting on the class (rather than a computed color) keeps the test fast and
+// independent of CSS loading, while still guarding the behaviour a user sees.
+describe("SimpleTable (React adapter) — row hover styling", () => {
+ it("applies the hover class to a row's cells on mouseenter when useHoverRowBackground is enabled", async () => {
+ const { host, firstCell } = await mountTable({ useHoverRowBackground: true });
+
+ firstCell.dispatchEvent(new MouseEvent("mouseenter", { bubbles: true }));
+ await wait(50);
+
+ expect(host.querySelectorAll(".st-cell.st-row-hovered").length).toBeGreaterThan(0);
+ });
+
+ it("removes the hover class when the pointer leaves the row", async () => {
+ const { host, firstCell } = await mountTable({ useHoverRowBackground: true });
+
+ firstCell.dispatchEvent(new MouseEvent("mouseenter", { bubbles: true }));
+ await wait(50);
+ expect(host.querySelectorAll(".st-cell.st-row-hovered").length).toBeGreaterThan(0);
+
+ firstCell.dispatchEvent(new MouseEvent("mouseleave", { bubbles: true }));
+ await wait(50);
+ expect(host.querySelectorAll(".st-cell.st-row-hovered").length).toBe(0);
+ });
+
+ it("does not apply the hover class when useHoverRowBackground is disabled", async () => {
+ const { host, firstCell } = await mountTable({ useHoverRowBackground: false });
+
+ firstCell.dispatchEvent(new MouseEvent("mouseenter", { bubbles: true }));
+ await wait(50);
+
+ expect(host.querySelectorAll(".st-cell.st-row-hovered").length).toBe(0);
+ });
+});
diff --git a/packages/react/vitest.config.ts b/packages/react/vitest.config.ts
new file mode 100644
index 000000000..b4b3cacdc
--- /dev/null
+++ b/packages/react/vitest.config.ts
@@ -0,0 +1,20 @@
+import { resolve } from "path";
+import { defineConfig } from "vitest/config";
+
+// Test the adapter against the core *source* (not the built dist) so tests run
+// without a prior build step. Mirrors the path alias in tsconfig.json.
+export default defineConfig({
+ resolve: {
+ alias: {
+ "simple-table-core": resolve(__dirname, "../core/src/index.ts"),
+ },
+ },
+ test: {
+ environment: "jsdom",
+ globals: true,
+ // The vanilla core imports a CSS bundle on load. We assert on DOM classes,
+ // not computed colors, so CSS processing is unnecessary here.
+ css: false,
+ setupFiles: ["./vitest.setup.ts"],
+ },
+});
diff --git a/packages/react/vitest.setup.ts b/packages/react/vitest.setup.ts
new file mode 100644
index 000000000..48e351443
--- /dev/null
+++ b/packages/react/vitest.setup.ts
@@ -0,0 +1,37 @@
+// jsdom does not implement ResizeObserver, but the core DimensionManager
+// constructs one when the table mounts and relies on its *initial* callback to
+// read the container width and trigger the first render. A no-op stub leaves
+// the table empty, so deliver one initial notification on observe().
+class ResizeObserverStub {
+ private callback: ResizeObserverCallback;
+ constructor(callback: ResizeObserverCallback) {
+ this.callback = callback;
+ }
+ observe(): void {
+ // Schedule on a microtask so the DimensionManager has finished wiring its
+ // subscriber before the (rAF-deferred) width notification fires.
+ queueMicrotask(() => this.callback([], this as unknown as ResizeObserver));
+ }
+ unobserve(): void {}
+ disconnect(): void {}
+}
+
+globalThis.ResizeObserver = ResizeObserverStub as unknown as typeof ResizeObserver;
+
+// The DimensionManager defers its width notification to requestAnimationFrame.
+// jsdom may not provide rAF, so polyfill it with a macrotask fallback.
+if (typeof globalThis.requestAnimationFrame !== "function") {
+ globalThis.requestAnimationFrame = ((cb: FrameRequestCallback) =>
+ setTimeout(() => cb(performance.now()), 0) as unknown as number) as typeof requestAnimationFrame;
+ globalThis.cancelAnimationFrame = ((id: number) =>
+ clearTimeout(id as unknown as ReturnType)) as typeof cancelAnimationFrame;
+}
+
+// jsdom reports 0 for all layout box metrics. Give elements a non-zero width so
+// the core's column virtualization renders body cells instead of an empty grid.
+Object.defineProperty(HTMLElement.prototype, "clientWidth", {
+ configurable: true,
+ get() {
+ return 800;
+ },
+});
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index af3b7dcc1..683332353 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -452,6 +452,9 @@ importers:
'@types/react-dom':
specifier: ^18.0.0
version: 18.3.7(@types/react@18.3.28)
+ jsdom:
+ specifier: ^24.1.3
+ version: 24.1.3
react:
specifier: ^18.0.0
version: 18.3.1
@@ -479,6 +482,9 @@ importers:
typescript:
specifier: ^4.9.5
version: 4.9.5
+ vitest:
+ specifier: ^2.1.9
+ version: 2.1.9(@types/node@20.19.39)(jsdom@24.1.3)(lightningcss@1.32.0)(terser@5.46.1)
packages/solid:
dependencies:
@@ -728,6 +734,9 @@ packages:
react: '>=19.0.0'
react-dom: '>=19.0.0'
+ '@asamuzakjp/css-color@3.2.0':
+ resolution: {integrity: sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==}
+
'@babel/code-frame@7.29.0':
resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==}
engines: {node: '>=6.9.0'}
@@ -3458,15 +3467,38 @@ packages:
'@vitest/expect@2.0.5':
resolution: {integrity: sha512-yHZtwuP7JZivj65Gxoi8upUN2OzHTi3zVfjwdpu2WrvCZPLwsJ2Ey5ILIPccoW23dd/zQBlJ4/dhi7DWNyXCpA==}
+ '@vitest/expect@2.1.9':
+ resolution: {integrity: sha512-UJCIkTBenHeKT1TTlKMJWy1laZewsRIzYighyYiJKZreqtdxSos/S1t+ktRMQWu2CKqaarrkeszJx1cgC5tGZw==}
+
+ '@vitest/mocker@2.1.9':
+ resolution: {integrity: sha512-tVL6uJgoUdi6icpxmdrn5YNo3g3Dxv+IHJBr0GXHaEdTcw3F+cPKnsXFhli6nO+f/6SDKPHEK1UN+k+TQv0Ehg==}
+ peerDependencies:
+ msw: ^2.4.9
+ vite: ^5.0.0
+ peerDependenciesMeta:
+ msw:
+ optional: true
+ vite:
+ optional: true
+
'@vitest/pretty-format@2.0.5':
resolution: {integrity: sha512-h8k+1oWHfwTkyTkb9egzwNMfJAEx4veaPSnMeKbVSjp4euqGSbQlm5+6VHwTr7u4FJslVVsUG5nopCaAYdOmSQ==}
'@vitest/pretty-format@2.1.9':
resolution: {integrity: sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ==}
+ '@vitest/runner@2.1.9':
+ resolution: {integrity: sha512-ZXSSqTFIrzduD63btIfEyOmNcBmQvgOVsPNPe0jYtESiXkhd8u2erDLnMxmGrDCwHCCHE7hxwRDCT3pt0esT4g==}
+
+ '@vitest/snapshot@2.1.9':
+ resolution: {integrity: sha512-oBO82rEjsxLNJincVhLhaxxZdEtV0EFHMK5Kmx5sJ6H9L183dHECjiefOAdnqpIgT5eZwT04PoggUnW88vOBNQ==}
+
'@vitest/spy@2.0.5':
resolution: {integrity: sha512-c/jdthAhvJdpfVuaexSrnawxZz6pywlTPe84LUB2m/4t3rl2fTo9NFGBG4oWgaD+FTgDDV8hJ/nibT7IfH3JfA==}
+ '@vitest/spy@2.1.9':
+ resolution: {integrity: sha512-E1B35FwzXXTs9FHNK6bDszs7mtydNi5MIfUWpceJ8Xbfb1gBMscAnwLbEu+B44ed6W3XjL9/ehLPHR1fkf1KLQ==}
+
'@vitest/utils@2.0.5':
resolution: {integrity: sha512-d8HKbqIcya+GR67mkZbrzhS5kKhtp8dQLcmRZLGTscGVg7yImT82cIrhtn2L8+VujWcy6KZweApgNmPsTAO/UQ==}
@@ -3573,6 +3605,10 @@ packages:
engines: {node: '>=0.4.0'}
hasBin: true
+ agent-base@7.1.4:
+ resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==}
+ engines: {node: '>= 14'}
+
aggregate-error@3.1.0:
resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==}
engines: {node: '>=8'}
@@ -3864,6 +3900,10 @@ packages:
resolution: {integrity: sha512-fey6+4jDK7TFtFg/klGSvNKJctyU7n2aQdnM+CO0ruLPbqqMOM8Tio0Pc+deqUeVKX1tL5DQep1zQ7+37aTAsA==}
engines: {node: '>= 0.8'}
+ cac@6.7.14:
+ resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==}
+ engines: {node: '>=8'}
+
caching-transform@4.0.0:
resolution: {integrity: sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==}
engines: {node: '>=8'}
@@ -4235,6 +4275,10 @@ packages:
resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==}
engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'}
+ cssstyle@4.6.0:
+ resolution: {integrity: sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==}
+ engines: {node: '>=18'}
+
csstype@3.2.3:
resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
@@ -4249,6 +4293,10 @@ packages:
resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==}
engines: {node: '>= 12'}
+ data-urls@5.0.0:
+ resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==}
+ engines: {node: '>=18'}
+
data-view-buffer@1.0.2:
resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==}
engines: {node: '>= 0.4'}
@@ -4291,6 +4339,9 @@ packages:
resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==}
engines: {node: '>=0.10.0'}
+ decimal.js@10.6.0:
+ resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==}
+
decode-named-character-reference@1.3.0:
resolution: {integrity: sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==}
@@ -4760,6 +4811,10 @@ packages:
resolution: {integrity: sha512-+kn8561vHAY+dt+0gMqqj1oY+g5xWrsuGMk4QGxotT2WS545nVqqjs37z6hrYfIuucwqthzwJfCJUEYqixyljg==}
deprecated: ⚠️ The 'expect-playwright' package is deprecated. The Playwright core assertions (via @playwright/test) now cover the same functionality. Please migrate to built-in expect. See https://playwright.dev/docs/test-assertions for migration.
+ expect-type@1.3.0:
+ resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==}
+ engines: {node: '>=12.0.0'}
+
expect@29.7.0:
resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
@@ -5087,6 +5142,10 @@ packages:
resolution: {integrity: sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==}
engines: {node: '>=0.10.0'}
+ html-encoding-sniffer@4.0.0:
+ resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==}
+ engines: {node: '>=18'}
+
html-entities@2.3.3:
resolution: {integrity: sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==}
@@ -5131,6 +5190,14 @@ packages:
htmlparser2@6.1.0:
resolution: {integrity: sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==}
+ http-proxy-agent@7.0.2:
+ resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==}
+ engines: {node: '>= 14'}
+
+ https-proxy-agent@7.0.6:
+ resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==}
+ engines: {node: '>= 14'}
+
human-signals@2.1.0:
resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==}
engines: {node: '>=10.17.0'}
@@ -5320,6 +5387,9 @@ packages:
resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==}
engines: {node: '>=0.10.0'}
+ is-potential-custom-element-name@1.0.1:
+ resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==}
+
is-reference@3.0.3:
resolution: {integrity: sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==}
@@ -5622,6 +5692,15 @@ packages:
resolution: {integrity: sha512-iZ8Bdb84lWRuGHamRXFyML07r21pcwBrLkHEuHgEY5UbCouBwv7ECknDRKzsQIXMiqpPymqtIf8TC/shYKB5rw==}
engines: {node: '>=12.0.0'}
+ jsdom@24.1.3:
+ resolution: {integrity: sha512-MyL55p3Ut3cXbeBEG7Hcv0mVM8pp8PBNWxRqchZnSfAiES1v1mRnMeFfaHWIPULpwsYfvO+ZmMZz5tGCnjzDUQ==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ canvas: ^2.11.2
+ peerDependenciesMeta:
+ canvas:
+ optional: true
+
jsesc@3.1.0:
resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==}
engines: {node: '>=6'}
@@ -5829,6 +5908,9 @@ packages:
lower-case@2.0.2:
resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==}
+ lru-cache@10.4.3:
+ resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
+
lru-cache@5.1.1:
resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
@@ -6147,6 +6229,9 @@ packages:
nth-check@2.1.1:
resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
+ nwsapi@2.2.23:
+ resolution: {integrity: sha512-7wfH4sLbt4M0gCDzGE6vzQBo0bfTKjU7Sfpqy/7gs1qBfYz2vEJH6vXcBKpO3+6Yu1telwd0t9HpyOoLEQQbIQ==}
+
nyc@15.1.0:
resolution: {integrity: sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==}
engines: {node: '>=8.9'}
@@ -6314,6 +6399,9 @@ packages:
resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
engines: {node: '>=8'}
+ pathe@1.1.2:
+ resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==}
+
pathval@2.0.1:
resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==}
engines: {node: '>= 14.16'}
@@ -6972,6 +7060,9 @@ packages:
resolution: {integrity: sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==}
engines: {node: '>=10'}
+ psl@1.15.0:
+ resolution: {integrity: sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==}
+
punycode@1.4.1:
resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==}
@@ -6986,6 +7077,9 @@ packages:
resolution: {integrity: sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==}
engines: {node: '>=0.6'}
+ querystringify@2.2.0:
+ resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==}
+
queue-microtask@1.2.3:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
@@ -7369,6 +7463,9 @@ packages:
require-main-filename@2.0.0:
resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==}
+ requires-port@1.0.0:
+ resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==}
+
resend@6.10.0:
resolution: {integrity: sha512-i7CwZpYj4Oho1RxsTpLcCUkO08+HiL4NXrm6jLJ2WzJ89UGI8eROSieLONJA3hnUrf1OYnCyfq5F6POnHUMv1Q==}
engines: {node: '>=20'}
@@ -7483,6 +7580,12 @@ packages:
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
hasBin: true
+ rrweb-cssom@0.7.1:
+ resolution: {integrity: sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==}
+
+ rrweb-cssom@0.8.0:
+ resolution: {integrity: sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==}
+
run-parallel@1.2.0:
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
@@ -7514,6 +7617,10 @@ packages:
resolution: {integrity: sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==}
engines: {node: '>=11.0.0'}
+ saxes@6.0.0:
+ resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==}
+ engines: {node: '>=v12.22.7'}
+
scheduler@0.23.2:
resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==}
@@ -7599,6 +7706,9 @@ packages:
resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==}
engines: {node: '>= 0.4'}
+ siginfo@2.0.0:
+ resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==}
+
signal-exit@3.0.7:
resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
@@ -7685,9 +7795,15 @@ packages:
resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==}
engines: {node: '>=10'}
+ stackback@0.0.2:
+ resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==}
+
standardwebhooks@1.0.0:
resolution: {integrity: sha512-BbHGOQK9olHPMvQNHWul6MYlrRTAOKn03rOe4A8O3CLWhNf4YHBqq2HJKKC+sfqpxiBY52pNeesD6jIiLDz8jg==}
+ std-env@3.10.0:
+ resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==}
+
stop-iteration-iterator@1.1.0:
resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==}
engines: {node: '>= 0.4'}
@@ -7896,6 +8012,9 @@ packages:
svix@1.88.0:
resolution: {integrity: sha512-vm/JrrUd3bVyBE+3L33TIyVSs8gS5fYx7lrISvKlDJXTYX1ACH4REX8P1tHxsSKoZi/rvifM1t0XRc5Vc45THw==}
+ symbol-tree@3.2.4:
+ resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
+
tailwindcss@4.2.2:
resolution: {integrity: sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q==}
@@ -7939,10 +8058,20 @@ packages:
tiny-invariant@1.3.3:
resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==}
+ tinybench@2.9.0:
+ resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==}
+
+ tinyexec@0.3.2:
+ resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==}
+
tinyglobby@0.2.15:
resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==}
engines: {node: '>=12.0.0'}
+ tinypool@1.1.1:
+ resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==}
+ engines: {node: ^18.0.0 || >=20.0.0}
+
tinyrainbow@1.2.0:
resolution: {integrity: sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==}
engines: {node: '>=14.0.0'}
@@ -7965,6 +8094,14 @@ packages:
resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==}
engines: {node: '>=6'}
+ tough-cookie@4.1.4:
+ resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==}
+ engines: {node: '>=6'}
+
+ tr46@5.1.1:
+ resolution: {integrity: sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==}
+ engines: {node: '>=18'}
+
tree-kill@1.2.2:
resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==}
hasBin: true
@@ -8109,6 +8246,10 @@ packages:
unist-util-visit@5.1.0:
resolution: {integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==}
+ universalify@0.2.0:
+ resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==}
+ engines: {node: '>= 4.0.0'}
+
universalify@2.0.1:
resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==}
engines: {node: '>= 10.0.0'}
@@ -8129,6 +8270,9 @@ packages:
uri-js@4.4.1:
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
+ url-parse@1.5.10:
+ resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==}
+
url@0.11.4:
resolution: {integrity: sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg==}
engines: {node: '>= 0.4'}
@@ -8167,6 +8311,11 @@ packages:
vfile@6.0.3:
resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==}
+ vite-node@2.1.9:
+ resolution: {integrity: sha512-AM9aQ/IPrW/6ENLQg3AGY4K1N2TGZdR5e4gu/MmmR2xR3Ll1+dib+nook92g4TV3PXVyeyxdWwtaCAiUL0hMxA==}
+ engines: {node: ^18.0.0 || >=20.0.0}
+ hasBin: true
+
vite-plugin-solid@2.11.11:
resolution: {integrity: sha512-YMZCXsLw9kyuvQFEdwLP27fuTQJLmjNoHy90AOJnbRuJ6DwShUxKFo38gdFrWn9v11hnGicKCZEaeI/TFs6JKw==}
peerDependencies:
@@ -8216,6 +8365,31 @@ packages:
vite:
optional: true
+ vitest@2.1.9:
+ resolution: {integrity: sha512-MSmPM9REYqDGBI8439mA4mWhV5sKmDlBKWIYbA3lRb2PTHACE0mgKwA8yQ2xq9vxDTuk4iPrECBAEW2aoFXY0Q==}
+ engines: {node: ^18.0.0 || >=20.0.0}
+ hasBin: true
+ peerDependencies:
+ '@edge-runtime/vm': '*'
+ '@types/node': ^18.0.0 || >=20.0.0
+ '@vitest/browser': 2.1.9
+ '@vitest/ui': 2.1.9
+ happy-dom: '*'
+ jsdom: '*'
+ peerDependenciesMeta:
+ '@edge-runtime/vm':
+ optional: true
+ '@types/node':
+ optional: true
+ '@vitest/browser':
+ optional: true
+ '@vitest/ui':
+ optional: true
+ happy-dom:
+ optional: true
+ jsdom:
+ optional: true
+
vue@3.5.31:
resolution: {integrity: sha512-iV/sU9SzOlmA/0tygSmjkEN6Jbs3nPoIPFhCMLD2STrjgOU8DX7ZtzMhg4ahVwf5Rp9KoFzcXeB1ZrVbLBp5/Q==}
peerDependencies:
@@ -8224,6 +8398,10 @@ packages:
typescript:
optional: true
+ w3c-xmlserializer@5.0.0:
+ resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==}
+ engines: {node: '>=18'}
+
wait-on@7.2.0:
resolution: {integrity: sha512-wCQcHkRazgjG5XoAq9jbTMLpNIjoSlZslrJ2+N9MxDsGEv1HnFoVjOCexL0ESva7Y9cu350j+DWADdk54s4AFQ==}
engines: {node: '>=12.0.0'}
@@ -8245,6 +8423,10 @@ packages:
resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==}
engines: {node: '>= 8'}
+ webidl-conversions@7.0.0:
+ resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==}
+ engines: {node: '>=12'}
+
webpack-bundle-analyzer@4.10.1:
resolution: {integrity: sha512-s3P7pgexgT/HTUSYgxJyn28A+99mmLq4HsJepMPzu0R8ImJc52QNqaFYW1Z2z2uIb1/J3eYgaAWVpaC+v/1aAQ==}
engines: {node: '>= 10.13.0'}
@@ -8288,6 +8470,10 @@ packages:
resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==}
engines: {node: '>=18'}
+ whatwg-url@14.2.0:
+ resolution: {integrity: sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==}
+ engines: {node: '>=18'}
+
which-boxed-primitive@1.1.1:
resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==}
engines: {node: '>= 0.4'}
@@ -8316,6 +8502,11 @@ packages:
engines: {node: '>= 8'}
hasBin: true
+ why-is-node-running@2.3.0:
+ resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==}
+ engines: {node: '>=8'}
+ hasBin: true
+
word-wrap@1.2.5:
resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
engines: {node: '>=0.10.0'}
@@ -8366,9 +8557,16 @@ packages:
utf-8-validate:
optional: true
+ xml-name-validator@5.0.0:
+ resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==}
+ engines: {node: '>=18'}
+
xml@1.0.1:
resolution: {integrity: sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==}
+ xmlchars@2.2.0:
+ resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==}
+
y18n@4.0.3:
resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==}
@@ -8562,6 +8760,14 @@ snapshots:
react: 19.2.5
react-dom: 19.2.5(react@19.2.5)
+ '@asamuzakjp/css-color@3.2.0':
+ dependencies:
+ '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)
+ '@csstools/css-color-parser': 3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)
+ '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4)
+ '@csstools/css-tokenizer': 3.0.4
+ lru-cache: 10.4.3
+
'@babel/code-frame@7.29.0':
dependencies:
'@babel/helper-validator-identifier': 7.28.5
@@ -11241,6 +11447,21 @@ snapshots:
chai: 5.3.3
tinyrainbow: 1.2.0
+ '@vitest/expect@2.1.9':
+ dependencies:
+ '@vitest/spy': 2.1.9
+ '@vitest/utils': 2.1.9
+ chai: 5.3.3
+ tinyrainbow: 1.2.0
+
+ '@vitest/mocker@2.1.9(vite@5.4.21(@types/node@20.19.39)(lightningcss@1.32.0)(terser@5.46.1))':
+ dependencies:
+ '@vitest/spy': 2.1.9
+ estree-walker: 3.0.3
+ magic-string: 0.30.21
+ optionalDependencies:
+ vite: 5.4.21(@types/node@20.19.39)(lightningcss@1.32.0)(terser@5.46.1)
+
'@vitest/pretty-format@2.0.5':
dependencies:
tinyrainbow: 1.2.0
@@ -11249,10 +11470,25 @@ snapshots:
dependencies:
tinyrainbow: 1.2.0
+ '@vitest/runner@2.1.9':
+ dependencies:
+ '@vitest/utils': 2.1.9
+ pathe: 1.1.2
+
+ '@vitest/snapshot@2.1.9':
+ dependencies:
+ '@vitest/pretty-format': 2.1.9
+ magic-string: 0.30.21
+ pathe: 1.1.2
+
'@vitest/spy@2.0.5':
dependencies:
tinyspy: 3.0.2
+ '@vitest/spy@2.1.9':
+ dependencies:
+ tinyspy: 3.0.2
+
'@vitest/utils@2.0.5':
dependencies:
'@vitest/pretty-format': 2.0.5
@@ -11420,6 +11656,8 @@ snapshots:
acorn@8.16.0: {}
+ agent-base@7.1.4: {}
+
aggregate-error@3.1.0:
dependencies:
clean-stack: 2.2.0
@@ -11801,6 +12039,8 @@ snapshots:
bytes-iec@3.1.1: {}
+ cac@6.7.14: {}
+
caching-transform@4.0.0:
dependencies:
hasha: 5.2.2
@@ -12242,6 +12482,11 @@ snapshots:
dependencies:
css-tree: 2.2.1
+ cssstyle@4.6.0:
+ dependencies:
+ '@asamuzakjp/css-color': 3.2.0
+ rrweb-cssom: 0.8.0
+
csstype@3.2.3: {}
cwd@0.10.0:
@@ -12253,6 +12498,11 @@ snapshots:
data-uri-to-buffer@4.0.1: {}
+ data-urls@5.0.0:
+ dependencies:
+ whatwg-mimetype: 4.0.0
+ whatwg-url: 14.2.0
+
data-view-buffer@1.0.2:
dependencies:
call-bound: 1.0.4
@@ -12287,6 +12537,8 @@ snapshots:
decamelize@1.2.0: {}
+ decimal.js@10.6.0: {}
+
decode-named-character-reference@1.3.0:
dependencies:
character-entities: 2.0.2
@@ -12704,8 +12956,8 @@ snapshots:
'@typescript-eslint/parser': 8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)
eslint: 9.39.4(jiti@2.6.1)
eslint-import-resolver-node: 0.3.10
- eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.4(jiti@2.6.1))
- eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.6.1))
+ eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1))
+ eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1))
eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.4(jiti@2.6.1))
eslint-plugin-react: 7.37.5(eslint@9.39.4(jiti@2.6.1))
eslint-plugin-react-hooks: 5.2.0(eslint@9.39.4(jiti@2.6.1))
@@ -12724,7 +12976,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
- eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.4(jiti@2.6.1)):
+ eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1)):
dependencies:
'@nolyfill/is-core-module': 1.0.39
debug: 4.4.3
@@ -12735,22 +12987,22 @@ snapshots:
tinyglobby: 0.2.15
unrs-resolver: 1.11.1
optionalDependencies:
- eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.6.1))
+ eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1))
transitivePeerDependencies:
- supports-color
- eslint-module-utils@2.12.1(@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.6.1)):
+ eslint-module-utils@2.12.1(@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1)):
dependencies:
debug: 3.2.7
optionalDependencies:
'@typescript-eslint/parser': 8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)
eslint: 9.39.4(jiti@2.6.1)
eslint-import-resolver-node: 0.3.10
- eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.4(jiti@2.6.1))
+ eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1))
transitivePeerDependencies:
- supports-color
- eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.6.1)):
+ eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1)):
dependencies:
'@rtsao/scc': 1.1.0
array-includes: 3.1.9
@@ -12761,7 +13013,7 @@ snapshots:
doctrine: 2.1.0
eslint: 9.39.4(jiti@2.6.1)
eslint-import-resolver-node: 0.3.10
- eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.6.1))
+ eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1))
hasown: 2.0.2
is-core-module: 2.16.1
is-glob: 4.0.3
@@ -12975,6 +13227,8 @@ snapshots:
expect-playwright@0.8.0: {}
+ expect-type@1.3.0: {}
+
expect@29.7.0:
dependencies:
'@jest/expect-utils': 29.7.0
@@ -13350,6 +13604,10 @@ snapshots:
dependencies:
parse-passwd: 1.0.0
+ html-encoding-sniffer@4.0.0:
+ dependencies:
+ whatwg-encoding: 3.1.1
+
html-entities@2.3.3: {}
html-entities@2.6.0: {}
@@ -13407,6 +13665,20 @@ snapshots:
domutils: 2.8.0
entities: 2.2.0
+ http-proxy-agent@7.0.2:
+ dependencies:
+ agent-base: 7.1.4
+ debug: 4.4.3
+ transitivePeerDependencies:
+ - supports-color
+
+ https-proxy-agent@7.0.6:
+ dependencies:
+ agent-base: 7.1.4
+ debug: 4.4.3
+ transitivePeerDependencies:
+ - supports-color
+
human-signals@2.1.0: {}
iconv-lite@0.6.3:
@@ -13573,6 +13845,8 @@ snapshots:
is-plain-object@5.0.0: {}
+ is-potential-custom-element-name@1.0.1: {}
+
is-reference@3.0.3:
dependencies:
'@types/estree': 1.0.8
@@ -14112,6 +14386,34 @@ snapshots:
jsdoc-type-pratt-parser@4.8.0: {}
+ jsdom@24.1.3:
+ dependencies:
+ cssstyle: 4.6.0
+ data-urls: 5.0.0
+ decimal.js: 10.6.0
+ form-data: 4.0.5
+ html-encoding-sniffer: 4.0.0
+ http-proxy-agent: 7.0.2
+ https-proxy-agent: 7.0.6
+ is-potential-custom-element-name: 1.0.1
+ nwsapi: 2.2.23
+ parse5: 7.3.0
+ rrweb-cssom: 0.7.1
+ saxes: 6.0.0
+ symbol-tree: 3.2.4
+ tough-cookie: 4.1.4
+ w3c-xmlserializer: 5.0.0
+ webidl-conversions: 7.0.0
+ whatwg-encoding: 3.1.1
+ whatwg-mimetype: 4.0.0
+ whatwg-url: 14.2.0
+ ws: 8.20.0
+ xml-name-validator: 5.0.0
+ transitivePeerDependencies:
+ - bufferutil
+ - supports-color
+ - utf-8-validate
+
jsesc@3.1.0: {}
json-buffer@3.0.1: {}
@@ -14265,6 +14567,8 @@ snapshots:
dependencies:
tslib: 2.8.1
+ lru-cache@10.4.3: {}
+
lru-cache@5.1.1:
dependencies:
yallist: 3.1.1
@@ -14747,6 +15051,8 @@ snapshots:
dependencies:
boolbase: 1.0.0
+ nwsapi@2.2.23: {}
+
nyc@15.1.0:
dependencies:
'@istanbuljs/load-nyc-config': 1.1.0
@@ -14963,6 +15269,8 @@ snapshots:
path-type@4.0.0: {}
+ pathe@1.1.2: {}
+
pathval@2.0.1: {}
picocolors@1.1.1: {}
@@ -15643,6 +15951,10 @@ snapshots:
proxy-from-env@2.1.0: {}
+ psl@1.15.0:
+ dependencies:
+ punycode: 2.3.1
+
punycode@1.4.1: {}
punycode@2.3.1: {}
@@ -15653,6 +15965,8 @@ snapshots:
dependencies:
side-channel: 1.1.0
+ querystringify@2.2.0: {}
+
queue-microtask@1.2.3: {}
randombytes@2.1.0:
@@ -16181,6 +16495,8 @@ snapshots:
require-main-filename@2.0.0: {}
+ requires-port@1.0.0: {}
+
resend@6.10.0:
dependencies:
postal-mime: 2.7.4
@@ -16342,6 +16658,10 @@ snapshots:
'@rollup/rollup-win32-x64-msvc': 4.60.0
fsevents: 2.3.3
+ rrweb-cssom@0.7.1: {}
+
+ rrweb-cssom@0.8.0: {}
+
run-parallel@1.2.0:
dependencies:
queue-microtask: 1.2.3
@@ -16377,6 +16697,10 @@ snapshots:
sax@1.6.0: {}
+ saxes@6.0.0:
+ dependencies:
+ xmlchars: 2.2.0
+
scheduler@0.23.2:
dependencies:
loose-envify: 1.4.0
@@ -16506,6 +16830,8 @@ snapshots:
side-channel-map: 1.0.1
side-channel-weakmap: 1.0.2
+ siginfo@2.0.0: {}
+
signal-exit@3.0.7: {}
sirv@2.0.4:
@@ -16610,11 +16936,15 @@ snapshots:
dependencies:
escape-string-regexp: 2.0.0
+ stackback@0.0.2: {}
+
standardwebhooks@1.0.0:
dependencies:
'@stablelib/base64': 1.0.1
fast-sha256: 1.3.0
+ std-env@3.10.0: {}
+
stop-iteration-iterator@1.1.0:
dependencies:
es-errors: 1.3.0
@@ -16835,6 +17165,8 @@ snapshots:
standardwebhooks: 1.0.0
uuid: 10.0.0
+ symbol-tree@3.2.4: {}
+
tailwindcss@4.2.2: {}
tapable@2.3.2: {}
@@ -16883,11 +17215,17 @@ snapshots:
tiny-invariant@1.3.3: {}
+ tinybench@2.9.0: {}
+
+ tinyexec@0.3.2: {}
+
tinyglobby@0.2.15:
dependencies:
fdir: 6.5.0(picomatch@4.0.4)
picomatch: 4.0.4
+ tinypool@1.1.1: {}
+
tinyrainbow@1.2.0: {}
tinyspy@3.0.2: {}
@@ -16902,6 +17240,17 @@ snapshots:
totalist@3.0.1: {}
+ tough-cookie@4.1.4:
+ dependencies:
+ psl: 1.15.0
+ punycode: 2.3.1
+ universalify: 0.2.0
+ url-parse: 1.5.10
+
+ tr46@5.1.1:
+ dependencies:
+ punycode: 2.3.1
+
tree-kill@1.2.2: {}
trim-lines@3.0.1: {}
@@ -17102,6 +17451,8 @@ snapshots:
unist-util-is: 6.0.1
unist-util-visit-parents: 6.0.2
+ universalify@0.2.0: {}
+
universalify@2.0.1: {}
unplugin@1.16.1:
@@ -17143,6 +17494,11 @@ snapshots:
dependencies:
punycode: 2.3.1
+ url-parse@1.5.10:
+ dependencies:
+ querystringify: 2.2.0
+ requires-port: 1.0.0
+
url@0.11.4:
dependencies:
punycode: 1.4.1
@@ -17184,6 +17540,24 @@ snapshots:
'@types/unist': 3.0.3
vfile-message: 4.0.3
+ vite-node@2.1.9(@types/node@20.19.39)(lightningcss@1.32.0)(terser@5.46.1):
+ dependencies:
+ cac: 6.7.14
+ debug: 4.4.3
+ es-module-lexer: 1.7.0
+ pathe: 1.1.2
+ vite: 5.4.21(@types/node@20.19.39)(lightningcss@1.32.0)(terser@5.46.1)
+ transitivePeerDependencies:
+ - '@types/node'
+ - less
+ - lightningcss
+ - sass
+ - sass-embedded
+ - stylus
+ - sugarss
+ - supports-color
+ - terser
+
vite-plugin-solid@2.11.11(@testing-library/jest-dom@6.5.0)(solid-js@1.9.12)(vite@5.4.21(@types/node@20.19.39)(lightningcss@1.32.0)(terser@5.46.1)):
dependencies:
'@babel/core': 7.29.0
@@ -17214,6 +17588,42 @@ snapshots:
optionalDependencies:
vite: 5.4.21(@types/node@20.19.39)(lightningcss@1.32.0)(terser@5.46.1)
+ vitest@2.1.9(@types/node@20.19.39)(jsdom@24.1.3)(lightningcss@1.32.0)(terser@5.46.1):
+ dependencies:
+ '@vitest/expect': 2.1.9
+ '@vitest/mocker': 2.1.9(vite@5.4.21(@types/node@20.19.39)(lightningcss@1.32.0)(terser@5.46.1))
+ '@vitest/pretty-format': 2.1.9
+ '@vitest/runner': 2.1.9
+ '@vitest/snapshot': 2.1.9
+ '@vitest/spy': 2.1.9
+ '@vitest/utils': 2.1.9
+ chai: 5.3.3
+ debug: 4.4.3
+ expect-type: 1.3.0
+ magic-string: 0.30.21
+ pathe: 1.1.2
+ std-env: 3.10.0
+ tinybench: 2.9.0
+ tinyexec: 0.3.2
+ tinypool: 1.1.1
+ tinyrainbow: 1.2.0
+ vite: 5.4.21(@types/node@20.19.39)(lightningcss@1.32.0)(terser@5.46.1)
+ vite-node: 2.1.9(@types/node@20.19.39)(lightningcss@1.32.0)(terser@5.46.1)
+ why-is-node-running: 2.3.0
+ optionalDependencies:
+ '@types/node': 20.19.39
+ jsdom: 24.1.3
+ transitivePeerDependencies:
+ - less
+ - lightningcss
+ - msw
+ - sass
+ - sass-embedded
+ - stylus
+ - sugarss
+ - supports-color
+ - terser
+
vue@3.5.31(typescript@4.9.5):
dependencies:
'@vue/compiler-dom': 3.5.31
@@ -17234,6 +17644,10 @@ snapshots:
optionalDependencies:
typescript: 5.9.3
+ w3c-xmlserializer@5.0.0:
+ dependencies:
+ xml-name-validator: 5.0.0
+
wait-on@7.2.0:
dependencies:
axios: 1.14.0
@@ -17263,6 +17677,8 @@ snapshots:
web-streams-polyfill@3.3.3: {}
+ webidl-conversions@7.0.0: {}
+
webpack-bundle-analyzer@4.10.1:
dependencies:
'@discoveryjs/json-ext': 0.5.7
@@ -17373,6 +17789,11 @@ snapshots:
whatwg-mimetype@4.0.0: {}
+ whatwg-url@14.2.0:
+ dependencies:
+ tr46: 5.1.1
+ webidl-conversions: 7.0.0
+
which-boxed-primitive@1.1.1:
dependencies:
is-bigint: 1.1.0
@@ -17424,6 +17845,11 @@ snapshots:
dependencies:
isexe: 2.0.0
+ why-is-node-running@2.3.0:
+ dependencies:
+ siginfo: 2.0.0
+ stackback: 0.0.2
+
word-wrap@1.2.5: {}
wrap-ansi@6.2.0:
@@ -17462,8 +17888,12 @@ snapshots:
ws@8.20.0: {}
+ xml-name-validator@5.0.0: {}
+
xml@1.0.1: {}
+ xmlchars@2.2.0: {}
+
y18n@4.0.3: {}
y18n@5.0.8: {}
From 90ade6930805d51aa3e21713cf1a699140ee9d43 Mon Sep 17 00:00:00 2001
From: peter <20213436+petera2c@users.noreply.github.com>
Date: Sun, 31 May 2026 10:10:08 -0500
Subject: [PATCH 2/4] Drag custom icon fix
---
packages/core/src/core/rendering/TableRenderer.ts | 2 ++
.../src/utils/columnEditor/createColumnEditor.ts | 8 ++++++++
.../utils/columnEditor/createColumnEditorPopout.ts | 5 +++++
.../src/utils/columnEditor/createColumnEditorRow.ts | 13 ++++++++++++-
4 files changed, 27 insertions(+), 1 deletion(-)
diff --git a/packages/core/src/core/rendering/TableRenderer.ts b/packages/core/src/core/rendering/TableRenderer.ts
index 43654af89..26f0a85b5 100644
--- a/packages/core/src/core/rendering/TableRenderer.ts
+++ b/packages/core/src/core/rendering/TableRenderer.ts
@@ -904,6 +904,7 @@ export class TableRenderer {
searchPlaceholder: mergedColumnEditorConfig.searchPlaceholder,
searchFunction: mergedColumnEditorConfig.searchFunction,
columnEditorConfig: mergedColumnEditorConfig,
+ icons: deps.resolvedIcons,
essentialAccessors: deps.essentialAccessors,
setHeaders: (newHeaders: HeaderObject[]) => {
deps.setHeaders(newHeaders);
@@ -929,6 +930,7 @@ export class TableRenderer {
searchPlaceholder: mergedColumnEditorConfig.searchPlaceholder,
searchFunction: mergedColumnEditorConfig.searchFunction,
columnEditorConfig: mergedColumnEditorConfig,
+ icons: deps.resolvedIcons,
essentialAccessors: deps.essentialAccessors,
setHeaders: (newHeaders: HeaderObject[]) => {
deps.setHeaders(newHeaders);
diff --git a/packages/core/src/utils/columnEditor/createColumnEditor.ts b/packages/core/src/utils/columnEditor/createColumnEditor.ts
index c998549f1..4f961aadb 100644
--- a/packages/core/src/utils/columnEditor/createColumnEditor.ts
+++ b/packages/core/src/utils/columnEditor/createColumnEditor.ts
@@ -2,6 +2,7 @@ import HeaderObject from "../../types/HeaderObject";
import { ColumnEditorSearchFunction, ColumnEditorConfig } from "../../types/ColumnEditorConfig";
import { createColumnEditorPopout } from "./createColumnEditorPopout";
import { ColumnVisibilityState } from "../../types/ColumnVisibilityTypes";
+import { IconsConfig } from "../../types/IconsConfig";
import { COLUMN_EDIT_WIDTH } from "../../consts/general-consts";
export interface CreateColumnEditorOptions {
@@ -13,6 +14,7 @@ export interface CreateColumnEditorOptions {
searchPlaceholder: string;
searchFunction?: ColumnEditorSearchFunction;
columnEditorConfig: ColumnEditorConfig;
+ icons?: IconsConfig;
essentialAccessors?: ReadonlySet;
resetColumns?: () => void;
setHeaders: (headers: HeaderObject[]) => void;
@@ -31,6 +33,7 @@ export const createColumnEditor = (options: CreateColumnEditorOptions) => {
searchPlaceholder,
searchFunction,
columnEditorConfig,
+ icons,
essentialAccessors,
resetColumns,
setHeaders,
@@ -69,6 +72,7 @@ export const createColumnEditor = (options: CreateColumnEditorOptions) => {
searchPlaceholder,
searchFunction,
columnEditorConfig,
+ icons,
essentialAccessors,
resetColumns,
setHeaders,
@@ -100,6 +104,9 @@ export const createColumnEditor = (options: CreateColumnEditorOptions) => {
if (newOptions.essentialAccessors !== undefined) {
essentialAccessors = newOptions.essentialAccessors;
}
+ if (newOptions.icons !== undefined) {
+ icons = newOptions.icons;
+ }
if (newOptions.resetColumns !== undefined) {
resetColumns = newOptions.resetColumns;
}
@@ -110,6 +117,7 @@ export const createColumnEditor = (options: CreateColumnEditorOptions) => {
searchPlaceholder: newOptions.searchPlaceholder,
searchFunction: newOptions.searchFunction,
columnEditorConfig: newOptions.columnEditorConfig,
+ icons: newOptions.icons,
essentialAccessors: newOptions.essentialAccessors,
resetColumns: newOptions.resetColumns,
setHeaders: newOptions.setHeaders,
diff --git a/packages/core/src/utils/columnEditor/createColumnEditorPopout.ts b/packages/core/src/utils/columnEditor/createColumnEditorPopout.ts
index f64811d65..d189511e1 100644
--- a/packages/core/src/utils/columnEditor/createColumnEditorPopout.ts
+++ b/packages/core/src/utils/columnEditor/createColumnEditorPopout.ts
@@ -4,6 +4,7 @@ import { ColumnEditorCustomRenderer } from "../../types/ColumnEditorCustomRender
import { FlattenedHeader } from "../../types/FlattenedHeader";
import { createColumnEditorRow } from "./createColumnEditorRow";
import { ColumnVisibilityState } from "../../types/ColumnVisibilityTypes";
+import { IconsConfig } from "../../types/IconsConfig";
import { partitionRootHeadersByPin, PanelSection } from "../../utils/pinnedColumnUtils";
export interface CreateColumnEditorPopoutOptions {
@@ -13,6 +14,7 @@ export interface CreateColumnEditorPopoutOptions {
searchPlaceholder: string;
searchFunction?: ColumnEditorSearchFunction;
columnEditorConfig: ColumnEditorConfig;
+ icons?: IconsConfig;
essentialAccessors?: ReadonlySet;
setHeaders: (headers: HeaderObject[]) => void;
onColumnVisibilityChange?: (state: ColumnVisibilityState) => void;
@@ -142,6 +144,7 @@ export const createColumnEditorPopout = (initialOptions: CreateColumnEditorPopou
searchPlaceholder,
searchFunction,
columnEditorConfig,
+ icons,
essentialAccessors,
setHeaders,
onColumnVisibilityChange,
@@ -355,6 +358,7 @@ export const createColumnEditorPopout = (initialOptions: CreateColumnEditorPopou
setExpandedHeaders,
setHoveredSeparatorIndex,
columnEditorConfig,
+ icons,
essentialAccessors: essentialAccessors ?? new Set(),
headers,
setHeaders,
@@ -405,6 +409,7 @@ export const createColumnEditorPopout = (initialOptions: CreateColumnEditorPopou
if (newOptions.searchPlaceholder !== undefined)
searchPlaceholder = newOptions.searchPlaceholder;
if (newOptions.searchFunction !== undefined) searchFunction = newOptions.searchFunction;
+ if (newOptions.icons !== undefined) icons = newOptions.icons;
if (newOptions.essentialAccessors !== undefined) essentialAccessors = newOptions.essentialAccessors;
if (newOptions.setHeaders !== undefined) setHeaders = newOptions.setHeaders;
if (newOptions.onColumnVisibilityChange !== undefined)
diff --git a/packages/core/src/utils/columnEditor/createColumnEditorRow.ts b/packages/core/src/utils/columnEditor/createColumnEditorRow.ts
index 629d4c2c6..9d0367035 100644
--- a/packages/core/src/utils/columnEditor/createColumnEditorRow.ts
+++ b/packages/core/src/utils/columnEditor/createColumnEditorRow.ts
@@ -1,5 +1,6 @@
import HeaderObject from "../../types/HeaderObject";
import { ColumnEditorConfig } from "../../types/ColumnEditorConfig";
+import { IconsConfig } from "../../types/IconsConfig";
import { ColumnEditorRowRendererComponents } from "../../types/ColumnEditorRowRendererProps";
import {
areAllChildrenHidden,
@@ -56,6 +57,8 @@ export interface CreateColumnEditorRowOptions {
setExpandedHeaders: (headers: Set) => void;
setHoveredSeparatorIndex: (index: number | null) => void;
columnEditorConfig: ColumnEditorConfig;
+ /** Resolved table icons; `icons.drag` overrides the default column-editor drag handle. */
+ icons?: IconsConfig;
essentialAccessors?: ReadonlySet;
headers: HeaderObject[];
setHeaders: (headers: HeaderObject[]) => void;
@@ -385,7 +388,15 @@ export const createColumnEditorRow = (options: CreateColumnEditorRowOptions): Cr
const dragIcon = document.createElement("div");
dragIcon.className = "st-drag-icon-container";
- dragIcon.innerHTML = DRAG_ICON_SVG;
+ const customDragIcon = options.icons?.drag;
+ if (typeof customDragIcon === "string") {
+ dragIcon.innerHTML = customDragIcon;
+ } else if (customDragIcon instanceof HTMLElement || customDragIcon instanceof SVGSVGElement) {
+ dragIcon.appendChild(customDragIcon.cloneNode(true));
+ } else {
+ dragIcon.innerHTML = DRAG_ICON_SVG;
+ }
+
const label = document.createElement("div");
label.className = "st-column-label-container";
From 112ec5ee14d58e9f716c8524a528624c4724a840 Mon Sep 17 00:00:00 2001
From: peter <20213436+petera2c@users.noreply.github.com>
Date: Sun, 31 May 2026 10:14:05 -0500
Subject: [PATCH 3/4] Footer position top
---
.../propDefinitions/simpleTableProps.ts | 9 ++++
.../angular/src/lib/SimpleTableComponent.ts | 2 +
packages/core/src/core/SimpleTableVanilla.ts | 4 ++
packages/core/src/core/dom/DOMManager.ts | 41 ++++++++++++++++++-
packages/core/src/index.ts | 2 +
packages/core/src/styles/base.css | 5 +++
packages/core/src/types/FooterPosition.ts | 4 ++
packages/core/src/types/SimpleTableConfig.ts | 3 ++
packages/core/src/types/SimpleTableProps.ts | 2 +
.../tests/24-FooterRendererTests.stories.ts | 35 +++++++++++++++-
10 files changed, 103 insertions(+), 4 deletions(-)
create mode 100644 packages/core/src/types/FooterPosition.ts
diff --git a/apps/marketing/src/constants/propDefinitions/simpleTableProps.ts b/apps/marketing/src/constants/propDefinitions/simpleTableProps.ts
index 0949e395d..823057e59 100644
--- a/apps/marketing/src/constants/propDefinitions/simpleTableProps.ts
+++ b/apps/marketing/src/constants/propDefinitions/simpleTableProps.ts
@@ -240,6 +240,15 @@ initialSortDirection="asc"`,
type: "boolean",
example: `hideFooter={true}`,
},
+ {
+ key: "footerPosition",
+ name: "footerPosition",
+ required: false,
+ description:
+ 'Placement of the pagination footer (built-in or footerRenderer). Use "top" to render above the table body; default is "bottom".',
+ type: '"top" | "bottom"',
+ example: `footerPosition="top"`,
+ },
{
key: "quickFilter",
name: "quickFilter",
diff --git a/packages/angular/src/lib/SimpleTableComponent.ts b/packages/angular/src/lib/SimpleTableComponent.ts
index 407881428..62acb936e 100644
--- a/packages/angular/src/lib/SimpleTableComponent.ts
+++ b/packages/angular/src/lib/SimpleTableComponent.ts
@@ -87,6 +87,7 @@ export class SimpleTableComponent implements OnInit, OnChanges, OnDestroy {
@Input() columnBorders?: SimpleTableAngularProps["columnBorders"];
@Input() rowButtons?: SimpleTableAngularProps["rowButtons"];
@Input() hideFooter?: SimpleTableAngularProps["hideFooter"];
+ @Input() footerPosition?: SimpleTableAngularProps["footerPosition"];
@Input() initialSortColumn?: SimpleTableAngularProps["initialSortColumn"];
@Input() initialSortDirection?: SimpleTableAngularProps["initialSortDirection"];
@Input() expandAll?: SimpleTableAngularProps["expandAll"];
@@ -194,6 +195,7 @@ export class SimpleTableComponent implements OnInit, OnChanges, OnDestroy {
if (this.columnBorders !== undefined) props.columnBorders = this.columnBorders;
if (this.rowButtons !== undefined) props.rowButtons = this.rowButtons;
if (this.hideFooter !== undefined) props.hideFooter = this.hideFooter;
+ if (this.footerPosition !== undefined) props.footerPosition = this.footerPosition;
if (this.initialSortColumn !== undefined) props.initialSortColumn = this.initialSortColumn;
if (this.initialSortDirection !== undefined)
props.initialSortDirection = this.initialSortDirection;
diff --git a/packages/core/src/core/SimpleTableVanilla.ts b/packages/core/src/core/SimpleTableVanilla.ts
index f9c9a665f..dc3999bd1 100644
--- a/packages/core/src/core/SimpleTableVanilla.ts
+++ b/packages/core/src/core/SimpleTableVanilla.ts
@@ -1165,6 +1165,10 @@ export class SimpleTableVanilla {
this.domManager.updateTheme(config.theme);
}
+ if (config.footerPosition !== undefined) {
+ this.domManager.syncFooterPosition(this.config.footerPosition);
+ }
+
if (config.customTheme !== undefined) {
const previousTheme = this.customTheme;
this.customTheme = TableInitializer.mergeCustomTheme(this.config);
diff --git a/packages/core/src/core/dom/DOMManager.ts b/packages/core/src/core/dom/DOMManager.ts
index 513e00b2d..d58d9396f 100644
--- a/packages/core/src/core/dom/DOMManager.ts
+++ b/packages/core/src/core/dom/DOMManager.ts
@@ -78,6 +78,11 @@ export class DOMManager {
const footerContainer = document.createElement("div");
footerContainer.id = "st-footer-container";
+ const footerAtTop = config.footerPosition === "top";
+ if (footerAtTop) {
+ rootElement.classList.add("st-footer-position-top");
+ }
+
const ariaLiveRegion = document.createElement("div");
ariaLiveRegion.setAttribute("aria-live", "polite");
ariaLiveRegion.setAttribute("aria-atomic", "true");
@@ -88,8 +93,13 @@ export class DOMManager {
contentWrapper.appendChild(content);
- wrapperContainer.appendChild(contentWrapper);
- wrapperContainer.appendChild(footerContainer);
+ if (footerAtTop) {
+ wrapperContainer.appendChild(footerContainer);
+ wrapperContainer.appendChild(contentWrapper);
+ } else {
+ wrapperContainer.appendChild(contentWrapper);
+ wrapperContainer.appendChild(footerContainer);
+ }
rootElement.appendChild(wrapperContainer);
rootElement.appendChild(ariaLiveRegion);
@@ -117,6 +127,33 @@ export class DOMManager {
root.className = `${classes} theme-${theme}`;
}
+ /** Reorders footer vs content when `footerPosition` changes after mount. */
+ syncFooterPosition(footerPosition: SimpleTableConfig["footerPosition"]): void {
+ if (!this.elements) return;
+
+ const { rootElement, wrapperContainer, contentWrapper, footerContainer } = this.elements;
+ const atTop = footerPosition === "top";
+ rootElement.classList.toggle("st-footer-position-top", atTop);
+
+ const scrollbar = wrapperContainer.querySelector(".st-horizontal-scrollbar-container");
+
+ if (atTop) {
+ if (footerContainer !== wrapperContainer.firstElementChild) {
+ wrapperContainer.insertBefore(footerContainer, contentWrapper);
+ }
+ if (scrollbar) {
+ wrapperContainer.appendChild(scrollbar);
+ }
+ return;
+ }
+
+ if (scrollbar && scrollbar.parentElement === wrapperContainer) {
+ wrapperContainer.insertBefore(footerContainer, scrollbar);
+ } else if (footerContainer !== wrapperContainer.lastElementChild) {
+ wrapperContainer.appendChild(footerContainer);
+ }
+ }
+
getElements(): DOMElements | null {
return this.elements;
}
diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts
index af8a972ba..82101a566 100644
--- a/packages/core/src/index.ts
+++ b/packages/core/src/index.ts
@@ -59,6 +59,7 @@ import type HeaderDropdownProps from "./types/HeaderDropdownProps";
import type { HeaderDropdown } from "./types/HeaderDropdownProps";
import type { RowButtonProps } from "./types/RowButton";
import type FooterRendererProps from "./types/FooterRendererProps";
+import type { FooterPosition } from "./types/FooterPosition";
import type {
LoadingStateRenderer,
ErrorStateRenderer,
@@ -117,6 +118,7 @@ export type {
ExportValueProps,
FilterCondition,
FooterRendererProps,
+ FooterPosition,
GetRowId,
GetRowIdParams,
IconsConfig,
diff --git a/packages/core/src/styles/base.css b/packages/core/src/styles/base.css
index e3b3a454b..cdf857a73 100644
--- a/packages/core/src/styles/base.css
+++ b/packages/core/src/styles/base.css
@@ -968,6 +968,11 @@ input {
gap: var(--st-spacing-medium);
}
+.simple-table-root.st-footer-position-top .st-footer {
+ border-top: none;
+ border-bottom: var(--st-border-width) solid var(--st-border-color);
+}
+
.st-footer-info {
display: flex;
align-items: center;
diff --git a/packages/core/src/types/FooterPosition.ts b/packages/core/src/types/FooterPosition.ts
new file mode 100644
index 000000000..f4a39a744
--- /dev/null
+++ b/packages/core/src/types/FooterPosition.ts
@@ -0,0 +1,4 @@
+/** Where the pagination footer (default or `footerRenderer`) is placed in the table chrome. */
+export type FooterPosition = "top" | "bottom";
+
+export default FooterPosition;
diff --git a/packages/core/src/types/SimpleTableConfig.ts b/packages/core/src/types/SimpleTableConfig.ts
index a28414c0f..7a22fabce 100644
--- a/packages/core/src/types/SimpleTableConfig.ts
+++ b/packages/core/src/types/SimpleTableConfig.ts
@@ -23,6 +23,7 @@ import { ColumnEditorConfig } from "./ColumnEditorConfig";
import { VanillaIconsConfig } from "./IconsConfig";
import { QuickFilterConfig } from "./QuickFilterTypes";
import { AnimationsConfig } from "./AnimationsConfig";
+import type { FooterPosition } from "./FooterPosition";
export interface SimpleTableConfig {
animations?: AnimationsConfig;
@@ -48,6 +49,8 @@ export interface SimpleTableConfig {
externalFilterHandling?: boolean;
externalSortHandling?: boolean;
footerRenderer?: (props: FooterRendererProps) => HTMLElement | string | null;
+ /** Placement of the pagination footer. Default `"bottom"`. */
+ footerPosition?: FooterPosition;
headerDropdown?: VanillaHeaderDropdown;
height?: string | number;
hideFooter?: boolean;
diff --git a/packages/core/src/types/SimpleTableProps.ts b/packages/core/src/types/SimpleTableProps.ts
index d7bd19d40..3cfcd0e30 100644
--- a/packages/core/src/types/SimpleTableProps.ts
+++ b/packages/core/src/types/SimpleTableProps.ts
@@ -24,6 +24,7 @@ import { ColumnEditorConfig } from "./ColumnEditorConfig";
import { IconsConfig } from "./IconsConfig";
import { QuickFilterConfig } from "./QuickFilterTypes";
import { AnimationsConfig } from "./AnimationsConfig";
+import type { FooterPosition } from "./FooterPosition";
export interface SimpleTableProps {
animations?: AnimationsConfig; // Cell animation configuration (FLIP-style on sort and programmatic column reorder). Defaults: enabled=true, duration=240ms, easing=cubic-bezier(0.2, 0.8, 0.2, 1).
@@ -49,6 +50,7 @@ export interface SimpleTableProps {
externalFilterHandling?: boolean; // Flag to let consumer handle filter logic completely
externalSortHandling?: boolean; // Flag to let consumer handle sort logic completely
footerRenderer?: (props: FooterRendererProps) => HTMLElement | string | null; // Custom footer renderer
+ footerPosition?: FooterPosition; // Pagination footer placement (default "bottom")
headerDropdown?: HeaderDropdown; // Custom dropdown component for headers
height?: string | number; // Height of the table
hideFooter?: boolean; // Flag for hiding the footer
diff --git a/packages/core/stories/tests/24-FooterRendererTests.stories.ts b/packages/core/stories/tests/24-FooterRendererTests.stories.ts
index 5a441bf23..00540c7ba 100644
--- a/packages/core/stories/tests/24-FooterRendererTests.stories.ts
+++ b/packages/core/stories/tests/24-FooterRendererTests.stories.ts
@@ -1,6 +1,6 @@
/**
* FOOTER RENDERER TESTS
- * Tests for table footer: default footer with pagination, hideFooter.
+ * Tests for table footer: default footer with pagination, hideFooter, footerPosition.
*/
import type { Meta } from "@storybook/html";
@@ -16,7 +16,7 @@ const meta: Meta = {
docs: {
description: {
component:
- "Tests for table footer: default footer with pagination info and navigation, hideFooter.",
+ "Tests for table footer: default footer with pagination info and navigation, hideFooter, footerPosition top.",
},
},
},
@@ -71,6 +71,37 @@ export const FooterShowsCorrectRange = {
},
};
+export const FooterPositionTop = {
+ render: () => {
+ const { wrapper } = renderVanillaTable(headers, createData(25), {
+ getRowId: (p) => String(p.row?.id),
+ height: "300px",
+ shouldPaginate: true,
+ rowsPerPage: 10,
+ footerPosition: "top",
+ });
+ return wrapper;
+ },
+ play: async ({ canvasElement }: { canvasElement: HTMLElement }) => {
+ await waitForTable();
+ const root = canvasElement.querySelector(".simple-table-root");
+ expect(root?.classList.contains("st-footer-position-top")).toBe(true);
+
+ const wrapperContainer = canvasElement.querySelector(".st-wrapper-container");
+ expect(wrapperContainer?.firstElementChild?.id).toBe("st-footer-container");
+
+ const footer = canvasElement.querySelector(".st-footer");
+ expect(footer).toBeTruthy();
+ expect(footer?.querySelector(".st-footer-pagination")).toBeTruthy();
+
+ const header = canvasElement.querySelector(".st-header-container");
+ const footerContainer = canvasElement.querySelector("#st-footer-container");
+ expect(footerContainer && header && footerContainer.compareDocumentPosition(header)).toBe(
+ Node.DOCUMENT_POSITION_FOLLOWING,
+ );
+ },
+};
+
export const HideFooterHidesFooter = {
render: () => {
const { wrapper } = renderVanillaTable(headers, createData(10), {
From 3315efb75be45b42204f7882d33c43535a8f7392 Mon Sep 17 00:00:00 2001
From: peter <20213436+petera2c@users.noreply.github.com>
Date: Sun, 31 May 2026 10:28:23 -0500
Subject: [PATCH 4/4] Row position className
---
.../docs-pages/FooterRendererContent.tsx | 17 ++++++++++++++
apps/marketing/src/constants/changelog.ts | 22 +++++++++++++++++++
packages/angular/package.json | 2 +-
packages/core/package.json | 2 +-
packages/core/src/utils/bodyCell/styling.ts | 4 ++++
.../core/src/utils/nestedGridRowRenderer.ts | 2 +-
packages/core/src/utils/stateRowRenderer.ts | 2 +-
packages/react/package.json | 2 +-
packages/solid/package.json | 2 +-
packages/svelte/package.json | 2 +-
packages/vue/package.json | 2 +-
11 files changed, 51 insertions(+), 8 deletions(-)
diff --git a/apps/marketing/src/components/pages/docs-pages/FooterRendererContent.tsx b/apps/marketing/src/components/pages/docs-pages/FooterRendererContent.tsx
index 585a2d5d8..9bd65a07c 100644
--- a/apps/marketing/src/components/pages/docs-pages/FooterRendererContent.tsx
+++ b/apps/marketing/src/components/pages/docs-pages/FooterRendererContent.tsx
@@ -99,6 +99,23 @@ const FooterRendererContent = () => {
footer.
+
+ Set{" "}
+
+ footerPosition="top"
+ {" "}
+ to render the footer above the table body instead of below it. This applies to both the
+ default footer and a custom{" "}
+
+ footerRenderer
+
+ , and defaults to{" "}
+
+ "bottom"
+
+ .
+
+
diff --git a/apps/marketing/src/constants/changelog.ts b/apps/marketing/src/constants/changelog.ts
index 02862afb6..6dcbd7aa5 100644
--- a/apps/marketing/src/constants/changelog.ts
+++ b/apps/marketing/src/constants/changelog.ts
@@ -10,6 +10,27 @@ export interface ChangelogEntry {
link?: string;
}[];
}
+export const v3_6_3: ChangelogEntry = {
+ version: "3.6.3",
+ date: "2026-05-31",
+ title: "footerPosition prop",
+ description:
+ 'New footerPosition prop renders the pagination footer (built-in or footerRenderer) above the table body when set to "top".',
+ changes: [
+ {
+ type: "feature",
+ description:
+ 'New footerPosition prop ("top" | "bottom", default "bottom") controls placement of the pagination footer.',
+ link: "/docs/footer-renderer",
+ },
+ {
+ type: "feature",
+ description:
+ "Added a st-row-position-{position} class to every rendered row (body cells, state rows, and nested-grid rows), letting consumers style any specific row via CSS (e.g. .st-row-position-3 { ... }).",
+ },
+ ],
+};
+
export const v3_6_2: ChangelogEntry = {
version: "3.6.2",
date: "2026-05-16",
@@ -1743,6 +1764,7 @@ export const v1_4_4: ChangelogEntry = {
// Array of all changelog entries (newest first)
export const CHANGELOG_ENTRIES: ChangelogEntry[] = [
+ v3_6_3,
v3_6_2,
v3_6_0,
v3_5_3,
diff --git a/packages/angular/package.json b/packages/angular/package.json
index 5e4b31a4d..b0a02b717 100644
--- a/packages/angular/package.json
+++ b/packages/angular/package.json
@@ -1,6 +1,6 @@
{
"name": "@simple-table/angular",
- "version": "3.6.2",
+ "version": "3.6.3",
"main": "dist/cjs/index.js",
"module": "dist/index.es.js",
"types": "dist/types/angular/src/index.d.ts",
diff --git a/packages/core/package.json b/packages/core/package.json
index b80a13410..21bc8c3ab 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -1,6 +1,6 @@
{
"name": "simple-table-core",
- "version": "3.6.2",
+ "version": "3.6.3",
"main": "dist/cjs/index.js",
"module": "dist/index.es.js",
"types": "dist/src/index.d.ts",
diff --git a/packages/core/src/utils/bodyCell/styling.ts b/packages/core/src/utils/bodyCell/styling.ts
index e7f6ca3eb..5df473ac7 100644
--- a/packages/core/src/utils/bodyCell/styling.ts
+++ b/packages/core/src/utils/bodyCell/styling.ts
@@ -111,6 +111,10 @@ const calculateBodyCellClasses = (cell: AbsoluteBodyCell, context: CellRenderCon
// Build class names
return [
"st-cell",
+ // Stable, scroll-independent row position so consumers can target any row
+ // (e.g. `.st-row-position-3`). Uses the absolute table position rather than
+ // the virtualized slice index, which changes as rows are reused on scroll.
+ `st-row-position-${cell.tableRow.position}`,
depth > 0 && header.expandable ? `st-cell-depth-${depth}` : "",
isIndividuallySelected
? isInitialFocused
diff --git a/packages/core/src/utils/nestedGridRowRenderer.ts b/packages/core/src/utils/nestedGridRowRenderer.ts
index 348041ae1..def73e265 100644
--- a/packages/core/src/utils/nestedGridRowRenderer.ts
+++ b/packages/core/src/utils/nestedGridRowRenderer.ts
@@ -91,7 +91,7 @@ export function createNestedGridRow(
});
const rowElement = document.createElement("div");
- rowElement.className = "st-row st-nested-grid-row";
+ rowElement.className = `st-row st-nested-grid-row st-row-position-${tableRow.position}`;
rowElement.dataset.index = String(tableRow.position);
rowElement.style.position = "absolute";
rowElement.style.left = "0";
diff --git a/packages/core/src/utils/stateRowRenderer.ts b/packages/core/src/utils/stateRowRenderer.ts
index 71fe32831..544319629 100644
--- a/packages/core/src/utils/stateRowRenderer.ts
+++ b/packages/core/src/utils/stateRowRenderer.ts
@@ -52,7 +52,7 @@ export const createStateRow = (
}
const rowElement = document.createElement("div");
- rowElement.className = "st-row st-state-row";
+ rowElement.className = `st-row st-state-row st-row-position-${position}`;
rowElement.dataset.index = String(context.index);
rowElement.style.width = "100%";
diff --git a/packages/react/package.json b/packages/react/package.json
index 6a7a47bc8..6d8996f3e 100644
--- a/packages/react/package.json
+++ b/packages/react/package.json
@@ -1,6 +1,6 @@
{
"name": "@simple-table/react",
- "version": "3.6.2",
+ "version": "3.6.3",
"main": "dist/cjs/index.js",
"module": "dist/index.es.js",
"types": "dist/types/react/src/index.d.ts",
diff --git a/packages/solid/package.json b/packages/solid/package.json
index db935c013..01a2d7e71 100644
--- a/packages/solid/package.json
+++ b/packages/solid/package.json
@@ -1,6 +1,6 @@
{
"name": "@simple-table/solid",
- "version": "3.6.2",
+ "version": "3.6.3",
"main": "dist/cjs/index.js",
"module": "dist/index.es.js",
"types": "dist/types/solid/src/index.d.ts",
diff --git a/packages/svelte/package.json b/packages/svelte/package.json
index 77033d882..f776563eb 100644
--- a/packages/svelte/package.json
+++ b/packages/svelte/package.json
@@ -1,6 +1,6 @@
{
"name": "@simple-table/svelte",
- "version": "3.6.2",
+ "version": "3.6.3",
"main": "dist/cjs/index.js",
"module": "dist/index.es.js",
"types": "dist/types/index.d.ts",
diff --git a/packages/vue/package.json b/packages/vue/package.json
index 741fbf1f1..b7a9df33e 100644
--- a/packages/vue/package.json
+++ b/packages/vue/package.json
@@ -1,6 +1,6 @@
{
"name": "@simple-table/vue",
- "version": "3.6.2",
+ "version": "3.6.3",
"main": "dist/cjs/index.js",
"module": "dist/index.es.js",
"types": "dist/types/vue/src/index.d.ts",