From ee4110fc9ff52aa00dcb016bc8ec63600ff7550f Mon Sep 17 00:00:00 2001 From: Mateusz Wojczal Date: Wed, 8 Jul 2026 10:45:46 +0200 Subject: [PATCH 01/29] runner: monorepo scaffold + catalog importer + migrate all 13 examples Deliverable 1 of the self-hosted demo runner that replaces CodeSandbox and, later, the render-ms redirect microservice. - config/frameworks.json: single source of truth (tier, wrappers, dev/build commands, ports) for all 13 examples. Angular is Tier 2 (needs a real Node dev server). 7 Tier-1 (Sandpack) / 6 Tier-2 (Cloudflare Sandbox containers). - pipeline/import.mjs: deterministic catalog importer; excludes build artifacts, lockfiles, CodeSandbox metadata, binaries; validates entries. - catalog.json: all 13 examples normalized to starting templates. - packages/runtime: DemoRuntime interface, resolveRuntime, and applyHandsontableVersion (ported from render-ms; pins core + wrapper in lockstep, never touches @handsontable/pikaday; pkg.pr.new supported). - packages/editor-shell: branding theme tokens (white-label). - pnpm workspace, tsconfig base, .env.example, docs/architecture.md. --- runner/.env.example | 36 ++ runner/.gitignore | 18 + runner/README.md | 48 ++ runner/catalog.json | 522 ++++++++++++++++++ runner/config/frameworks.json | 174 ++++++ runner/docs/architecture.md | 140 +++++ runner/package.json | 21 + runner/packages/editor-shell/package.json | 13 + runner/packages/editor-shell/src/index.ts | 5 + runner/packages/editor-shell/src/theme.ts | 43 ++ runner/packages/editor-shell/tsconfig.json | 8 + runner/packages/runtime/package.json | 24 + runner/packages/runtime/src/index.ts | 24 + runner/packages/runtime/src/resolveRuntime.ts | 26 + runner/packages/runtime/src/types.ts | 61 ++ runner/packages/runtime/src/version.ts | 147 +++++ runner/packages/runtime/tsconfig.json | 8 + runner/pipeline/import.mjs | 204 +++++++ runner/pnpm-workspace.yaml | 5 + runner/tsconfig.base.json | 18 + 20 files changed, 1545 insertions(+) create mode 100644 runner/.env.example create mode 100644 runner/.gitignore create mode 100644 runner/README.md create mode 100644 runner/catalog.json create mode 100644 runner/config/frameworks.json create mode 100644 runner/docs/architecture.md create mode 100644 runner/package.json create mode 100644 runner/packages/editor-shell/package.json create mode 100644 runner/packages/editor-shell/src/index.ts create mode 100644 runner/packages/editor-shell/src/theme.ts create mode 100644 runner/packages/editor-shell/tsconfig.json create mode 100644 runner/packages/runtime/package.json create mode 100644 runner/packages/runtime/src/index.ts create mode 100644 runner/packages/runtime/src/resolveRuntime.ts create mode 100644 runner/packages/runtime/src/types.ts create mode 100644 runner/packages/runtime/src/version.ts create mode 100644 runner/packages/runtime/tsconfig.json create mode 100644 runner/pipeline/import.mjs create mode 100644 runner/pnpm-workspace.yaml create mode 100644 runner/tsconfig.base.json diff --git a/runner/.env.example b/runner/.env.example new file mode 100644 index 0000000..0f00442 --- /dev/null +++ b/runner/.env.example @@ -0,0 +1,36 @@ +# Demo runner — environment. Copy to .env and fill in. NEVER commit real secrets. +# All secrets live in environment variables / Cloudflare secrets only. + +# --- Cloudflare account / API --- +CLOUDFLARE_ACCOUNT_ID= +CLOUDFLARE_API_TOKEN= + +# --- R2 (source snapshots + built artifacts) --- +R2_BUCKET=handsontable-demos +R2_ACCESS_KEY_ID= +R2_SECRET_ACCESS_KEY= + +# --- D1 (demo metadata + short ids) --- +D1_DATABASE_ID= + +# --- KV (edge read-cache for demo JSON) --- +KV_NAMESPACE_ID= + +# --- Cloudflare Access (gates authoring app + all write endpoints) --- +CF_ACCESS_TEAM_DOMAIN= +CF_ACCESS_AUD= + +# --- Domains --- +PUBLIC_VIEWER_ORIGIN=https://demos.handsontable.com +AUTHORING_ORIGIN= + +# --- Tier-1 bundler --- +# Phase 1: Sandpack hosted bundler (default, leave blank). +# Phase 2: self-hosted classic bundler URL (see docs/self-host-bundler.md). +SANDPACK_BUNDLER_URL= + +# --- Handsontable version policy --- +HANDSONTABLE_MAX_MAJOR=19 + +# --- GitHub (render-ms compatibility shim / example fetch, if needed) --- +GITHUB_TOKEN= diff --git a/runner/.gitignore b/runner/.gitignore new file mode 100644 index 0000000..4d7e729 --- /dev/null +++ b/runner/.gitignore @@ -0,0 +1,18 @@ +node_modules/ +dist/ +.dist/ +build/ +.wrangler/ +.output/ +.next/ +.nuxt/ +.astro/ +*.tsbuildinfo + +# secrets +.env +.env.local +.dev.vars + +# os +.DS_Store diff --git a/runner/README.md b/runner/README.md new file mode 100644 index 0000000..f064cc8 --- /dev/null +++ b/runner/README.md @@ -0,0 +1,48 @@ +# Handsontable Demo Runner + +Self-hosted system that renders every Handsontable example live at any Handsontable +version, lets internal team members edit code on the fly, and produces clean, +permanent client URLs to share — replacing CodeSandbox and, eventually, the +`render-ms` redirect microservice. + +> Status: **work in progress.** This directory is being built deliverable by +> deliverable. See `docs/architecture.md` for the full design and +> [`../render-ms`](../render-ms) for the service this will replace (kept running +> until the new system fully takes over). + +## One UX, two engines + +A single editor shell binds only to the `DemoRuntime` interface. Which engine +sits behind it is invisible to the author: + +- **Tier 1 — client-side (Sandpack, in-browser bundler, free):** + `example1`, `javascript`, `typescript`, `react`, `react-js`, `ant-design`, `vue`. +- **Tier 2 — SSR / meta-framework (Cloudflare Sandbox container running the real + dev server):** `angular`, `next.js`, `next-shadcn.js`, `astro`, `nuxt`, `remix`. + +## Layout + +| Path | Purpose | +|------|---------| +| `config/frameworks.json` | Single source of truth: tier, wrappers, dev/build commands, ports per example. | +| `catalog.json` | Generated. All 13 examples normalized into starting templates. | +| `pipeline/` | `import.mjs` (catalog generation), build-and-serve snapshotter (later). | +| `packages/runtime/` | `DemoRuntime` interface, `applyHandsontableVersion`, `resolveRuntime`. | +| `packages/editor-shell/` | Framework-agnostic editor UI + branding `theme.ts`. | +| `apps/authoring/` | Vite+React authoring app (behind Cloudflare Access). | +| `apps/viewer/` | Public read-only viewer for `/d/:id`. | +| `workers/api/` | Sharing + Tier-2 orchestration Worker. | +| `containers/` | One Dockerfile per Tier-2 framework (deps baked in). | +| `scripts/` | Migration + warming. | +| `docs/` | Architecture, self-host-bundler, run/deploy, non-technical guide. | + +## Regenerate the catalog + +```bash +node pipeline/import.mjs # reads ../examples/, writes catalog.json +``` + +## Constraints + +No Nodebox / WebContainers. White-label (no CodeSandbox marks). Secrets in env +only. Private repo under `handsontable`. See `docs/architecture.md`. diff --git a/runner/catalog.json b/runner/catalog.json new file mode 100644 index 0000000..f43d865 --- /dev/null +++ b/runner/catalog.json @@ -0,0 +1,522 @@ +{ + "generatedFrom": "handsontable/examples examples/", + "supportedHandsontableMajors": [ + 15, + 16, + 17, + 18, + 19 + ], + "tiers": { + "1": "client-side (Sandpack, in-browser bundler)", + "2": "SSR/meta-framework (Cloudflare Sandbox container)" + }, + "examples": [ + { + "framework": "example1", + "displayName": "Vanilla (Parcel)", + "tier": 1, + "sandpackTemplate": "vanilla-ts", + "container": null, + "htWrappers": [], + "entry": "/index.ts", + "htmlEntry": "/index.html", + "devCommand": null, + "buildCommand": "parcel build index.html", + "outputDir": "dist", + "outputGlob": null, + "staticExport": false, + "spaMode": false, + "port": null, + "installCommand": "npm install", + "htCoreRange": "latest", + "fileCount": 4, + "assets": [], + "skipped": [], + "files": { + "/index.html": "\n\n \n \n \n Handsontable\n \n \n\n \n
\n \n \n\n", + "/index.ts": "import Handsontable from \"handsontable/base\";\nimport \"handsontable/dist/handsontable.full.min.css\";\nimport { registerAllModules } from \"handsontable/registry\";\n\n// Register all available Handsontable modules\nregisterAllModules();\n\n\n const data = [\n {\n id: 640329,\n itemName: \"Lunar Core\",\n itemNo: \"XJ-12\",\n leadEngineer: \"Ellen Ripley\",\n cost: 350000,\n inStock: true,\n category: \"Lander\",\n itemQuality: 87,\n origin: \"🇺🇸 USA\",\n quantity: 2,\n valueStock: 700000,\n repairable: false,\n supplierName: \"TechNova\",\n restockDate: \"2025-08-01\",\n operationalStatus: \"Awaiting Parts\",\n },\n {\n id: 863104,\n itemName: \"Zero Thrusters\",\n itemNo: \"QL-54\",\n leadEngineer: \"Sam Bell\",\n cost: 450000,\n inStock: false,\n category: \"Propulsion\",\n itemQuality: 0,\n origin: \"🇩🇪 Germany\",\n quantity: 0,\n valueStock: 0,\n repairable: true,\n supplierName: \"PropelMax\",\n restockDate: \"2025-09-15\",\n operationalStatus: \"In Maintenance\",\n },\n {\n id: 395603,\n itemName: \"EVA Suits\",\n itemNo: \"PM-67\",\n leadEngineer: \"Alex Rogan\",\n cost: 150000,\n inStock: true,\n category: \"Equipment\",\n itemQuality: 79,\n origin: \"🇮🇹 Italy\",\n quantity: 50,\n valueStock: 7500000,\n repairable: true,\n supplierName: \"SuitCraft\",\n restockDate: \"2025-10-05\",\n operationalStatus: \"Ready for Testing\",\n },\n {\n id: 679083,\n itemName: \"Solar Panels\",\n itemNo: \"BW-09\",\n leadEngineer: \"Dave Bowman\",\n cost: 75000,\n inStock: true,\n category: \"Energy\",\n itemQuality: 95,\n origin: \"🇺🇸 USA\",\n quantity: 10,\n valueStock: 750000,\n repairable: false,\n supplierName: \"SolarStream\",\n restockDate: \"2025-11-10\",\n operationalStatus: \"Operational\",\n },\n {\n id: 912663,\n itemName: \"Comm Array\",\n itemNo: \"ZR-56\",\n leadEngineer: \"Louise Banks\",\n cost: 125000,\n inStock: false,\n category: \"Communication\",\n itemQuality: 0,\n origin: \"🇯🇵 Japan\",\n quantity: 0,\n valueStock: 0,\n repairable: true,\n supplierName: \"CommTech\",\n restockDate: \"2025-12-20\",\n operationalStatus: \"Decommissioned\",\n },\n {\n id: 315806,\n itemName: \"Habitat Dome\",\n itemNo: \"UJ-23\",\n leadEngineer: \"Dr. Ryan Stone\",\n cost: 1000000,\n inStock: true,\n category: \"Shelter\",\n itemQuality: 93,\n origin: \"🇨🇦 Canada\",\n quantity: 3,\n valueStock: 3000000,\n repairable: false,\n supplierName: \"DomeInnovate\",\n restockDate: \"2026-01-25\",\n operationalStatus: \"Operational\",\n },\n {\n id: 954632,\n itemName: \"Oxygen Unit\",\n itemNo: \"FK-87\",\n leadEngineer: \"Dr. Grace Augustine\",\n cost: 600000,\n inStock: true,\n category: \"Life Support\",\n itemQuality: 85,\n origin: \"🇺🇸 USA\",\n quantity: 15,\n valueStock: 9000000,\n repairable: true,\n supplierName: \"OxyGenius\",\n restockDate: \"2026-03-02\",\n operationalStatus: \"Awaiting Parts\",\n },\n {\n id: 734944,\n itemName: \"Processing Rig\",\n itemNo: \"LK-13\",\n leadEngineer: \"Jake Sully\",\n cost: 350000,\n inStock: true,\n category: \"Mining\",\n itemQuality: 81,\n origin: \"🇦🇺 Australia\",\n quantity: 25,\n valueStock: 8750000,\n repairable: true,\n supplierName: \"RigTech\",\n restockDate: \"2026-04-15\",\n operationalStatus: \"Ready for Testing\",\n },\n {\n id: 834662,\n itemName: \"Navigation Module\",\n itemNo: \"XP-24\",\n leadEngineer: \"Dr. Ellie Arroway\",\n cost: 450000,\n inStock: true,\n category: \"Navigation\",\n itemQuality: 89,\n origin: \"🇫🇷 France\",\n quantity: 8,\n valueStock: 3600000,\n repairable: false,\n supplierName: \"NavSolutions\",\n restockDate: \"2026-05-30\",\n operationalStatus: \"In Maintenance\",\n },\n {\n id: 714329,\n itemName: \"Surveyor Arm\",\n itemNo: \"QA-86\",\n leadEngineer: \"Mark Watney\",\n cost: 100000,\n inStock: true,\n category: \"Exploration\",\n itemQuality: 78,\n origin: \"🇺🇸 USA\",\n quantity: 40,\n valueStock: 4000000,\n repairable: true,\n supplierName: \"ExploreTech\",\n restockDate: \"2026-07-12\",\n operationalStatus: \"Decommissioned\",\n },\n {\n id: 291439,\n itemName: \"Habitat Dome\",\n itemNo: \"UJ-24\",\n leadEngineer: \"Jane Doe\",\n cost: 1100000,\n inStock: true,\n category: \"Shelter\",\n itemQuality: 92,\n origin: \"🇬🇧 UK\",\n quantity: 4,\n valueStock: 4400000,\n repairable: false,\n supplierName: \"DomeInnovate\",\n restockDate: \"2026-02-01\",\n operationalStatus: \"Operational\",\n },\n {\n id: 485199,\n itemName: \"Power Generator\",\n itemNo: \"PG-11\",\n leadEngineer: \"John Smith\",\n cost: 500000,\n inStock: true,\n category: \"Energy\",\n itemQuality: 85,\n origin: \"🇨🇳 China\",\n quantity: 7,\n valueStock: 3500000,\n repairable: true,\n supplierName: \"PowerCo\",\n restockDate: \"2026-03-10\",\n operationalStatus: \"Operational\",\n },\n {\n id: 271418,\n itemName: \"Life Support System\",\n itemNo: \"LS-22\",\n leadEngineer: \"Mike Johnson\",\n cost: 800000,\n inStock: false,\n category: \"Life Support\",\n itemQuality: 80,\n origin: \"🇯🇵 Japan\",\n quantity: 0,\n valueStock: 0,\n repairable: true,\n supplierName: \"LifeTech\",\n restockDate: \"2026-04-20\",\n operationalStatus: \"Awaiting Parts\",\n },\n {\n id: 390776,\n itemName: \"Mars Rover\",\n itemNo: \"MR-33\",\n leadEngineer: \"Anna Davis\",\n cost: 2000000,\n inStock: true,\n category: \"Exploration\",\n itemQuality: 90,\n origin: \"🇮🇳 India\",\n quantity: 5,\n valueStock: 10000000,\n repairable: false,\n supplierName: \"RoverWorks\",\n restockDate: \"2026-05-15\",\n operationalStatus: \"Operational\",\n },\n {\n id: 672342,\n itemName: \"Hydroponics Module\",\n itemNo: \"HM-44\",\n leadEngineer: \"Robert Brown\",\n cost: 450000,\n inStock: true,\n category: \"Life Support\",\n itemQuality: 88,\n origin: \"🇦🇺 Australia\",\n quantity: 6,\n valueStock: 2700000,\n repairable: true,\n supplierName: \"GreenGrow\",\n restockDate: \"2026-06-25\",\n operationalStatus: \"Operational\",\n },\n {\n id: 907454,\n itemName: \"Satellite Dish\",\n itemNo: \"SD-55\",\n leadEngineer: \"Emily Wilson\",\n cost: 300000,\n inStock: false,\n category: \"Communication\",\n itemQuality: 70,\n origin: \"🇺🇸 USA\",\n quantity: 0,\n valueStock: 0,\n repairable: true,\n supplierName: \"CommTech\",\n restockDate: \"2026-07-05\",\n operationalStatus: \"Decommissioned\",\n },\n {\n id: 841637,\n itemName: \"Thermal Regulator\",\n itemNo: \"TR-66\",\n leadEngineer: \"Olivia Taylor\",\n cost: 650000,\n inStock: true,\n category: \"Energy\",\n itemQuality: 82,\n origin: \"🇷🇺 Russia\",\n quantity: 8,\n valueStock: 5200000,\n repairable: false,\n supplierName: \"ThermoTech\",\n restockDate: \"2026-08-15\",\n operationalStatus: \"Operational\",\n },\n {\n id: 947335,\n itemName: \"Landing Gear\",\n itemNo: \"LG-77\",\n leadEngineer: \"David Lee\",\n cost: 250000,\n inStock: true,\n category: \"Lander\",\n itemQuality: 75,\n origin: \"🇫🇷 France\",\n quantity: 10,\n valueStock: 2500000,\n repairable: false,\n supplierName: \"LandingWorks\",\n restockDate: \"2026-09-20\",\n operationalStatus: \"Operational\",\n },\n {\n id: 629849,\n itemName: \"Radiation Shield\",\n itemNo: \"RS-88\",\n leadEngineer: \"Sophia Martinez\",\n cost: 900000,\n inStock: true,\n category: \"Equipment\",\n itemQuality: 95,\n origin: \"🇧🇷 Brazil\",\n quantity: 3,\n valueStock: 2700000,\n repairable: false,\n supplierName: \"ShieldPro\",\n restockDate: \"2026-10-30\",\n operationalStatus: \"Operational\",\n },\n {\n id: 519304,\n itemName: \"Fuel Cell\",\n itemNo: \"FC-99\",\n leadEngineer: \"James Anderson\",\n cost: 550000,\n inStock: true,\n category: \"Propulsion\",\n itemQuality: 89,\n origin: \"🇨🇦 Canada\",\n quantity: 5,\n valueStock: 2750000,\n repairable: true,\n supplierName: \"FuelWorks\",\n restockDate: \"2026-11-22\",\n operationalStatus: \"Operational\",\n },\n ];\n \n\nconst container = document.querySelector(\"#handsontable-grid\");\n\n\nnew Handsontable(container, {\n data,\n colHeaders: [\n \"ID\",\n \"Item Name\",\n \"Item No.\",\n \"Lead Engineer\",\n \"Cost\",\n \"In Stock\",\n \"Category\",\n \"Item Quality\",\n \"Origin\",\n \"Quantity\",\n \"Value Stock\",\n \"Repairable\",\n \"Supplier Name\",\n \"Restock Date\",\n \"Operational Status\",\n ],\n rowHeaders: true,\n height: 340,\n width: 800,\n autoWrapRow: true,\n autoWrapCol: true,\n dropdownMenu: true,\n filters: true,\n multiColumnSorting: true,\n hiddenColumns: {\n columns: [0, 2], // Hides the ID and Item No. columns\n indicators: true,\n },\n columns: [\n { data: \"id\", type: \"numeric\", width: 150 },\n {\n data: \"itemName\",\n type: \"text\",\n headerClassName: \"htLeft\",\n className: \"htLeft\",\n width: 150,\n },\n {\n data: \"itemNo\",\n type: \"text\",\n headerClassName: \"htLeft\",\n className: \"htLeft\",\n width: 150,\n }, // Hidden column\n {\n data: \"leadEngineer\",\n type: \"text\",\n headerClassName: \"htLeft\",\n className: \"htLeft\",\n width: 150,\n },\n {\n data: \"cost\",\n type: \"numeric\",\n numericFormat: { pattern: \"$0 0\" },\n headerClassName: \"htRight\",\n className: \"htRight\",\n width: 150,\n },\n {\n data: \"inStock\",\n type: \"checkbox\",\n headerClassName: \"htCenter\",\n className: \"htCenter\",\n width: 100,\n },\n {\n data: \"category\",\n type: \"text\",\n headerClassName: \"htLeft\",\n className: \"htLeft\",\n width: 150,\n },\n {\n data: \"itemQuality\",\n type: \"numeric\",\n numericFormat: { pattern: \"0%\" },\n headerClassName: \"htRight\",\n className: \"htRight\",\n width: 150,\n },\n {\n data: \"origin\",\n type: \"text\",\n headerClassName: \"htLeft\",\n className: \"htLeft\",\n width: 150,\n },\n {\n data: \"quantity\",\n type: \"numeric\",\n headerClassName: \"htRight\",\n className: \"htRight\",\n width: 150,\n },\n {\n data: \"valueStock\",\n type: \"numeric\",\n numericFormat: { pattern: \"$0 0\" },\n headerClassName: \"htRight\",\n className: \"htRight\",\n width: 150,\n },\n {\n data: \"repairable\",\n type: \"checkbox\",\n headerClassName: \"htCenter\",\n className: \"htCenter\",\n width: 100,\n },\n {\n data: \"supplierName\",\n type: \"text\",\n headerClassName: \"htLeft\",\n className: \"htLeft\",\n width: 150,\n },\n {\n data: \"restockDate\",\n type: \"date\",\n dateFormat: \"YYYY-MM-DD\",\n headerClassName: \"htRight\",\n className: \"htRight\",\n width: 150,\n },\n {\n data: \"operationalStatus\",\n type: \"text\",\n headerClassName: \"htLeft\",\n className: \"htLeft\",\n width: 180,\n },\n ],\n licenseKey: \"non-commercial-and-evaluation\",\n});\n", + "/package.json": "{\n \"name\": \"handsontable-pr-javascript-demo\",\n \"version\": \"1.0.0\",\n \"description\": \"\",\n \"main\": \"index.html\",\n \"private\": true,\n \"scripts\": {\n \"start\": \"parcel index.html --open\",\n \"build\": \"parcel build index.html\"\n },\n \"dependencies\": {\n \"handsontable\": \"latest\"\n },\n \"devDependencies\": {\n \"parcel-bundler\": \"latest\",\n \"typescript\": \"^5.9.3\"\n },\n \"keywords\": [],\n \"config\": {\n \"stackblitz\": {\n \"template\": \"typescript\"\n }\n }\n}\n", + "/styles.css": "" + } + }, + { + "framework": "javascript", + "displayName": "JavaScript (Vite)", + "tier": 1, + "sandpackTemplate": "vanilla", + "container": null, + "htWrappers": [], + "entry": "/index.js", + "htmlEntry": "/index.html", + "devCommand": null, + "buildCommand": "vite build", + "outputDir": "dist", + "outputGlob": null, + "staticExport": false, + "spaMode": false, + "port": null, + "installCommand": "npm install", + "htCoreRange": "18.0.0", + "fileCount": 10, + "assets": [], + "skipped": [], + "files": { + "/LICENSE.txt": "Copyright (c) HANDSONCODE sp. z o. o.\n\nHANDSONTABLE is a software distributed by HANDSONCODE sp. z o. o.,\na Polish corporation, based in Gdynia, Poland, at 96/98 Aleja Zwycięstwa,\nregistered with the National Court Register under number 538651,\nEU tax ID number: PL5862294002, share capital: PLN 62,800.00.\n\nThis software is protected by applicable copyright laws, including\ninternational treaties, and dual-licensed – depending on whether\nyour use is intended for or may result in commercial advantage\nor monetary compensation (commercial purposes), or not.\n\nIf your use involves only such purposes as research, private study,\nevaluation and the like, you agree to be bound by the terms included\nin the \"handsontable-non-commercial-license.pdf\" file, available\nin the main directory of this software repository.\n\nBy installing, copying, or otherwise using this software for\ncommercial purposes, you agree to be bound by the terms included\nin the \"handsontable-general-terms.pdf\" file, available in the main\ndirectory of this software repository.\n\nHANDSONCODE PROVIDES THIS SOFTWARE ON AN \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND. IN NO EVENT\nAND UNDER NO LEGAL THEORY, SHALL HANDSONCODE BE LIABLE\nTO YOU FOR DAMAGES, INCLUDING ANY DIRECT, INDIRECT, SPECIAL,\nINCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER ARISING\nFROM USE OR INABILITY TO USE THIS SOFTWARE.\n", + "/README.md": "# Demo\n\n## Description\n\nA general purpose demo showcasing some of the most popular features of Handsontable in JavaScript.\n\n## Installation\n\nInstall all necessary dependencies from npm with `npm install`.\n\n## Development\n\nTo start local development server use `npm run start`. You can then visit http://localhost:8080 to view the project.\n\n## Testing\n\nTo run tests for this project, first launch the development server with `npm run start`. Once the server is running, execute `npm run test` to run the test specs.\n\n## Forking\n\nThere are two ways to fork this project in Handsontable's monorepo:\n\n1. Fork the entire repository.\n2. Copy the example to a new repository. Detailed instructions are available [here](../../../../README.md#copying-an-example-to-a-separate-repo) in the section _Copying an example to a separate repo_.\n\n## License\n\nHandsontable is a commercial software with two licenses available:\n\n- Free for non-commercial purposes such as teaching, academic research, and evaluation. [Read it here](https://github.com/handsontable/handsontable/blob/master/handsontable-non-commercial-license.pdf).\n- Commercial license with support and maintenance included. See [pricing plans](https://handsontable.com/pricing).\n\n## Get help\n\nJoin our [community](https://github.com/handsontable/handsontable/discussions) to get help, share ideas, and contribute to the ongoing development of Handsontable!\n\nCommercial license holders with an active support plan can contact our [Support Team](https://handsontable.com/contact?category=technical_support).\n", + "/index.html": "\n\n\n\n Handsontable for Javascript example\n \n\t\n\n\n\n
\n \n\n\n\n", + "/index.js": "import Handsontable from 'handsontable/base';\nimport 'handsontable/styles/handsontable.min.css';\nimport 'handsontable/styles/ht-theme-main.min.css';\n\nimport { generateExampleData, isArabicDemoEnabled } from './src/utils';\nimport './src/styles.css';\nimport { registerLanguageDictionary, arAR } from 'handsontable/i18n';\n\n// choose cell types you want to use and import them\nimport {\n registerCellType,\n CheckboxCellType,\n DateCellType,\n DropdownCellType,\n NumericCellType,\n} from 'handsontable/cellTypes';\n\nimport {\n registerPlugin,\n AutoColumnSize,\n ContextMenu,\n CopyPaste,\n DropdownMenu,\n Filters,\n HiddenColumns,\n HiddenRows,\n ManualRowMove,\n MultiColumnSorting,\n UndoRedo,\n} from 'handsontable/plugins';\n\n// Import and register the Comments plugin\nimport { Comments } from 'handsontable/plugins/comments';\nregisterPlugin(Comments);\n\n// register imported cell types and plugins\nregisterPlugin(AutoColumnSize);\nregisterPlugin(ContextMenu);\nregisterPlugin(CopyPaste);\nregisterPlugin(DropdownMenu);\nregisterPlugin(Filters);\nregisterPlugin(HiddenColumns);\nregisterPlugin(HiddenRows);\nregisterPlugin(ManualRowMove);\nregisterPlugin(MultiColumnSorting);\nregisterPlugin(UndoRedo);\n\n// register imported cell types and plugins\nregisterCellType(DateCellType);\nregisterCellType(DropdownCellType);\nregisterCellType(CheckboxCellType);\nregisterCellType(NumericCellType);\n\nregisterLanguageDictionary(arAR);\n\nimport { alignHeaders, addClassesToRows } from './src/hooksCallbacks';\n\nconst example = document.getElementById('example');\n\nnew Handsontable(example, {\n data: generateExampleData(),\n themeName: 'ht-theme-main',\n layoutDirection: isArabicDemoEnabled() ? 'rtl' : 'ltr',\n language: isArabicDemoEnabled() ? arAR.languageCode : 'en-US',\n height: 248,\n width: 587,\n comments: true,\n colWidths: 150,\n colHeaders: [\n 'Company name',\n 'Name',\n 'Sell date',\n 'In stock',\n 'Qty',\n 'Order ID',\n 'Country',\n ],\n columns: [\n { data: 1, type: 'text' },\n { data: 3, type: 'text' },\n {\n data: 4,\n type: 'date',\n allowInvalid: false,\n dateFormat: isArabicDemoEnabled() ? 'M/D/YYYY' : 'DD/MM/YYYY',\n },\n {\n data: 6,\n type: 'checkbox',\n className: 'htCenter',\n },\n {\n data: 7,\n type: 'numeric',\n },\n { data: 5, type: 'text' },\n { data: 2, type: 'text' },\n ],\n cell: [{ row: 1, col: 1, comment: { value: 'Some cool comment here' } }],\n dropdownMenu: true,\n hiddenColumns: {\n indicators: true,\n },\n contextMenu: true,\n multiColumnSorting: true,\n filters: true,\n rowHeaders: true,\n manualRowMove: true,\n navigableHeaders: true,\n autoWrapCol: true,\n afterGetColHeader: alignHeaders,\n beforeRenderer: addClassesToRows,\n licenseKey: 'non-commercial-and-evaluation',\n});\n\nconsole.log(\n `Handsontable: v${Handsontable.version} (${Handsontable.buildDate})`\n);\n", + "/package.json": "{\n \"name\": \"vanilla-demo\",\n \"private\": true,\n \"version\": \"0.0.0\",\n \"description\": \"This is a general purpose demo that showcases the most popular features of Handsontable\",\n \"homepage\": \"https://handsontable.com/\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/handsontable/handsontable.git\"\n },\n \"bugs\": {\n \"url\": \"https://github.com/handsontable/handsontable/issues\"\n },\n \"author\": \"Handsoncode \",\n \"scripts\": {\n \"dev\": \"vite --port 8080\",\n \"build\": \"vite build\",\n \"preview\": \"vite preview\"\n },\n \"dependencies\": {\n \"@handsontable/pikaday\": \"^1.0.0\",\n \"handsontable\": \"18.0.0\"\n },\n \"devDependencies\": {\n \"vite\": \"^8.1.1\"\n },\n \"license\": \"SEE LICENSE IN LICENSE.txt\",\n \"config\": {\n \"codesandbox\": {\n \"templateId\": \"vmjhgx\"\n },\n \"stackblitz\": {\n \"template\": \"javascript\"\n }\n }\n}\n", + "/src/constants.js": "export const data = [\n [\n false,\n 'Tagcat',\n 'United Kingdom',\n 'Classic Vest',\n '2020-10-11',\n '01-2331942',\n true,\n '172',\n 2,\n 2,\n ],\n [true, 'Zoomzone', 'Indonesia', 'Cycling Cap', '2020-05-03', '88-2768633', true, '188', 6, 2],\n [\n true,\n 'Meeveo',\n 'United States',\n 'Full-Finger Gloves',\n '2020-03-27',\n '51-6775945',\n true,\n '162',\n 1,\n 3,\n ],\n [\n false,\n 'Buzzdog',\n 'Philippines',\n 'HL Mountain Frame',\n '2020-08-29',\n '44-4028109',\n true,\n '133',\n 7,\n 1,\n ],\n [true, 'Katz', 'India', 'Half-Finger Gloves', '2020-10-02', '08-2758492', true, '87', 1, 3],\n [false, 'Jaxbean', 'China', 'HL Road Frame', '2020-09-28', '84-3557705', false, '26', 8, 1],\n [false, 'Wikido', 'Brazil', 'HL Touring Frame', '2020-06-24', '20-9397637', false, '110', 4, 1],\n [\n false,\n 'Browsedrive',\n 'United States',\n 'LL Mountain Frame',\n '2020-03-13',\n '36-0079556',\n true,\n '50',\n 4,\n 4,\n ],\n [\n false,\n 'Twinder',\n 'United Kingdom',\n 'LL Road Frame',\n '2020-04-06',\n '41-1489542',\n false,\n '160',\n 6,\n 1,\n ],\n [false, 'Jetwire', 'China', 'LL Touring Frame', '2020-02-01', '37-1531629', true, '30', 8, 5],\n [\n false,\n 'Chatterpoint',\n 'China',\n 'Long-Sleeve Logo Jersey',\n '2020-07-14',\n '25-5083429',\n true,\n '39',\n 7,\n 2,\n ],\n [false, 'Twinder', 'Egypt', \"Men's Bib-Shorts\", '2020-08-31', '04-4281278', false, '96', 6, 1],\n [\n false,\n 'Midel',\n 'United States',\n \"Men's Sports Shorts\",\n '2020-06-27',\n '55-1711908',\n true,\n '108',\n 10,\n 3,\n ],\n [false, 'Yodo', 'India', 'ML Mountain Frame', '2020-03-16', '58-8360815', false, '46', 1, 1],\n [false, 'Camido', 'Russia', 'ML Mountain Frame-W', '2020-09-13', '10-3786104', true, '97', 8, 3],\n [false, 'Eire', 'Thailand', 'ML Road Frame', '2020-04-10', '45-1186054', true, '161', 1, 4],\n [\n false,\n 'Vinte',\n 'United Kingdom',\n 'ML Road Frame-W',\n '2020-01-22',\n '62-6202742',\n true,\n '58',\n 4,\n 3,\n ],\n [\n false,\n 'Twitterlist',\n 'China',\n 'Mountain Bike Socks',\n '2020-11-09',\n '88-9646223',\n true,\n '92',\n 8,\n 3,\n ],\n [false, 'Eidel', 'Bangladesh', 'Mountain-100', '2020-09-19', '45-5588112', true, '5', 6, 5],\n [false, 'Trunyx', 'Nigeria', 'Mountain-200', '2020-03-09', '66-6271819', true, '158', 4, 1],\n [false, 'Katz', 'Turkey', 'Mountain-300', '2020-03-05', '38-9245023', false, '121', 5, 4],\n [\n false,\n 'Kaymbo',\n 'United States',\n 'Mountain-400-W',\n '2020-12-24',\n '44-5916927',\n false,\n '61',\n 5,\n 4,\n ],\n [false, 'Ozu', 'Pakistan', 'Mountain-500', '2020-06-13', '31-5449914', true, '155', 2, 2],\n [false, 'Rhynyx', 'India', 'Racing Socks', '2020-12-05', '19-9413869', true, '162', 2, 4],\n [false, 'Flashset', 'Iran', 'Road-150', '2020-12-14', '25-9807605', false, '46', 7, 1],\n [false, 'Yata', 'Congo (Kinshasa)', 'Road-250', '2020-06-12', '74-4291983', true, '47', 4, 4],\n [false, 'Brainlounge', 'Vietnam', 'Road-350-W', '2020-03-10', '83-0980643', true, '104', 2, 3],\n [\n false,\n 'Babblestorm',\n 'United States',\n 'Road-450',\n '2020-10-10',\n '19-2878430',\n true,\n '101',\n 6,\n 4,\n ],\n [false, 'Youspan', 'Brazil', 'Road-550-W', '2020-12-16', '19-1838230', true, '150', 10, 3],\n [false, 'Nlounge', 'China', 'Road-650', '2020-10-31', '32-2267938', true, '42', 4, 2],\n [false, 'Twinte', 'India', 'Road-750', '2020-08-17', '79-2821972', true, '144', 9, 3],\n [\n false,\n 'Oyonder',\n 'United Kingdom',\n 'Short-Sleeve Classic Jersey',\n '2020-12-04',\n '46-6597557',\n true,\n '195',\n 4,\n 1,\n ],\n [false, 'Gigabox', 'Pakistan', 'Sport-100', '2020-02-03', '15-1793960', true, '199', 4, 4],\n [false, 'Livetube', 'France', 'Touring-1000', '2020-05-16', '86-0811003', true, '110', 4, 5],\n [false, 'Voomm', 'United Kingdom', 'Touring-2000', '2020-07-15', '95-3068680', true, '51', 4, 4],\n [false, 'Voonyx', 'China', 'Touring-3000', '2020-11-27', '35-3085360', false, '69', 2, 5],\n [\n false,\n 'Zoombeat',\n 'United States',\n \"Women's Mountain Shorts\",\n '2020-11-03',\n '56-8673088',\n false,\n '53',\n 2,\n 3,\n ],\n [false, 'Roomm', 'China', \"Women's Tights\", '2020-03-16', '76-0085918', true, '168', 1, 1],\n [false, 'Leenti', 'China', 'Mountain-400', '2020-05-16', '03-0893276', false, '58', 1, 4],\n [false, 'Jetpulse', 'United States', 'Road-550', '2020-02-08', '79-9013306', true, '152', 9, 3],\n [false, 'Katz', 'Peru', 'Road-350', '2020-02-15', '55-7799920', true, '66', 4, 2],\n [\n false,\n 'Cogidoo',\n 'India',\n 'LL Mountain Front Wheel',\n '2020-06-04',\n '07-0881122',\n false,\n '112',\n 9,\n 2,\n ],\n [\n false,\n 'Divavu',\n 'Colombia',\n 'Touring Rear Wheel',\n '2020-02-24',\n '58-6157387',\n true,\n '50',\n 10,\n 4,\n ],\n [false, 'Mydeo', 'China', 'Touring Front Wheel', '2020-12-07', '12-2810010', false, '31', 3, 5],\n [\n false,\n 'Browsebug',\n 'Japan',\n 'ML Mountain Front Wheel',\n '2020-01-14',\n '64-9249984',\n true,\n '132',\n 5,\n 5,\n ],\n [false, 'Layo', 'China', 'HL Mountain Front Wheel', '2020-04-24', '45-0739652', true, '45', 1, 5],\n [\n false,\n 'Snaptags',\n 'United Kingdom',\n 'LL Touring Handlebars',\n '2020-08-06',\n '09-5712761',\n true,\n '197',\n 4,\n 2,\n ],\n [\n false,\n 'Cogilith',\n 'China',\n 'HL Touring Handlebars',\n '2020-05-31',\n '01-7345008',\n true,\n '190',\n 4,\n 3,\n ],\n [\n false,\n 'Reallinks',\n 'United Kingdom',\n 'LL Road Front Wheel',\n '2020-05-14',\n '62-1065350',\n true,\n '184',\n 3,\n 4,\n ],\n [\n false,\n 'Quaxo',\n 'United States',\n 'ML Road Front Wheel',\n '2020-03-23',\n '44-7241323',\n true,\n '169',\n 3,\n 4,\n ],\n [false, 'Devify', 'China', 'HL Road Front Wheel', '2020-12-12', '52-0295699', false, '152', 4, 4],\n [\n false,\n 'Youopia',\n 'Angola',\n 'LL Mountain Handlebars',\n '2020-04-01',\n '52-2650922',\n false,\n '182',\n 6,\n 4,\n ],\n [false, 'Ainyx', 'China', 'Touring Pedal', '2020-02-27', '48-3618525', true, '141', 6, 1],\n [\n false,\n 'Browsetype',\n 'Malaysia',\n 'ML Mountain Handlebars',\n '2020-04-28',\n '51-8893923',\n true,\n '169',\n 7,\n 1,\n ],\n [false, 'Muxo', 'China', 'HL Mountain Handlebars', '2020-08-22', '68-5911361', false, '39', 7, 1],\n [\n false,\n 'Bubbletube',\n 'China',\n 'LL Road Handlebars',\n '2020-10-04',\n '41-5880042',\n true,\n '71',\n 8,\n 3,\n ],\n [false, 'Fadeo', 'Vietnam', 'ML Road Handlebars', '2020-04-23', '90-5913983', true, '148', 10, 3],\n [\n false,\n 'Yadel',\n 'United Kingdom',\n 'HL Road Handlebars',\n '2020-04-18',\n '92-0960699',\n true,\n '116',\n 8,\n 1,\n ],\n [false, 'Blognation', 'China', 'LL Headset', '2020-01-10', '06-9493898', true, '96', 10, 1],\n [false, 'Devpoint', 'China', 'ML Headset', '2020-12-25', '69-5878565', true, '35', 4, 2],\n [false, 'Aibox', 'United Kingdom', 'HL Headset', '2020-03-18', '13-1133017', true, '16', 8, 2],\n [false, 'Brightdog', 'China', 'LL Mountain Pedal', '2020-09-11', '39-6530433', true, '194', 2, 5],\n [false, 'Gabcube', 'Nigeria', 'ML Mountain Pedal', '2020-04-22', '96-6860388', true, '24', 1, 3],\n [false, 'Muxo', 'China', 'HL Mountain Pedal', '2020-06-05', '30-0356137', true, '170', 4, 4],\n [\n false,\n 'Tambee',\n 'China',\n 'ML Touring Seat/Saddle',\n '2020-02-22',\n '93-9058255',\n true,\n '184',\n 9,\n 5,\n ],\n [\n false,\n 'Cogilith',\n 'India',\n 'LL Touring Seat/Saddle',\n '2020-04-06',\n '82-9268909',\n false,\n '153',\n 10,\n 4,\n ],\n [\n false,\n 'Dynabox',\n 'Hong Kong',\n 'HL Touring Seat/Saddle',\n '2020-01-10',\n '20-6913815',\n false,\n '88',\n 10,\n 1,\n ],\n [false, 'Shuffledrive', 'Sudan', 'LL Road Pedal', '2020-09-16', '08-8238817', true, '57', 9, 2],\n [false, 'Fivechat', 'China', 'ML Road Pedal', '2020-08-26', '44-7370350', false, '62', 4, 1],\n [\n false,\n 'Meembee',\n 'United States',\n 'HL Road Pedal',\n '2020-12-27',\n '01-3525949',\n true,\n '123',\n 2,\n 4,\n ],\n [\n false,\n 'Dynazzy',\n 'United Kingdom',\n 'LL Mountain Seat/Saddle 1',\n '2020-12-15',\n '04-2414623',\n true,\n '77',\n 10,\n 5,\n ],\n [\n false,\n 'Eare',\n 'China',\n 'ML Mountain Seat/Saddle 1',\n '2020-04-04',\n '15-1917509',\n false,\n '199',\n 9,\n 4,\n ],\n [\n false,\n 'Yozio',\n 'China',\n 'HL Mountain Seat/Saddle 1',\n '2020-03-15',\n '06-2526845',\n true,\n '149',\n 8,\n 2,\n ],\n [false, 'Quinu', \"Xi'an\", '425-777-7829', '2020-02-22', '83-1713558', false, '191', 9, 5],\n [\n false,\n 'Jazzy',\n 'United Kingdom',\n 'ML Road Seat/Saddle 1',\n '2020-08-07',\n '00-8892524',\n true,\n '150',\n 10,\n 2,\n ],\n [\n false,\n 'Thoughtsphere',\n 'China',\n 'HL Road Seat/Saddle 1',\n '2020-11-28',\n '39-5538991',\n true,\n '130',\n 7,\n 3,\n ],\n [false, 'Leenti', 'China', 'ML Road Rear Wheel', '2020-12-29', '06-9002973', true, '179', 1, 2],\n [\n false,\n 'Quaxo',\n 'United Kingdom',\n 'HL Road Rear Wheel',\n '2020-09-06',\n '73-6104901',\n true,\n '98',\n 5,\n 3,\n ],\n [\n false,\n 'Tanoodle',\n 'Chile',\n 'LL Mountain Seat/Saddle 2',\n '2020-05-24',\n '68-7384479',\n true,\n '175',\n 2,\n 3,\n ],\n [\n false,\n 'Feednation',\n 'China',\n 'ML Mountain Seat/Saddle 2',\n '2020-11-21',\n '26-7757763',\n true,\n '11',\n 1,\n 3,\n ],\n [\n false,\n 'Kayveo',\n 'China',\n 'HL Mountain Seat/Saddle 2',\n '2020-06-21',\n '07-4873562',\n false,\n '184',\n 7,\n 4,\n ],\n [\n false,\n 'Meevee',\n 'Saudi Arabia',\n 'LL Road Seat/Saddle 1',\n '2020-11-16',\n '46-5819554',\n false,\n '27',\n 9,\n 3,\n ],\n [\n false,\n 'Twitterworks',\n 'China',\n 'ML Road Seat/Saddle 2',\n '2020-04-19',\n '01-2666826',\n true,\n '186',\n 3,\n 2,\n ],\n [\n false,\n 'Wikizz',\n 'Tanzania',\n 'HL Road Seat/Saddle 2',\n '2020-03-08',\n '54-7090503',\n true,\n '20',\n 3,\n 3,\n ],\n [\n false,\n 'Yoveo',\n 'United States',\n 'LL Mountain Tire',\n '2020-10-14',\n '78-7658520',\n false,\n '153',\n 2,\n 1,\n ],\n [false, 'Yakidoo', 'China', 'ML Mountain Tire', '2020-10-12', '23-9926318', true, '161', 8, 5],\n [false, 'Oyope', 'China', 'HL Mountain Tire', '2020-09-20', '20-0179517', true, '98', 10, 5],\n [\n false,\n 'Skipstorm',\n 'United States',\n 'LL Road Tire',\n '2020-10-01',\n '02-9543343',\n true,\n '30',\n 7,\n 3,\n ],\n [false, 'Minyx', 'United States', 'ML Road Tire', '2020-07-07', '98-3938169', true, '73', 10, 2],\n [false, 'Miboo', 'China', 'HL Road Tire', '2020-07-25', '68-5197934', true, '158', 9, 1],\n [\n false,\n 'Realfire',\n 'United States',\n 'Touring Tire',\n '2020-08-27',\n '39-8260460',\n true,\n '122',\n 5,\n 2,\n ],\n [\n false,\n 'Shufflester',\n 'China',\n 'Mountain Tire Tube',\n '2020-06-08',\n '45-9776170',\n true,\n '33',\n 2,\n 4,\n ],\n [false, 'Ntag', 'China', 'Road Tire Tube', '2020-12-06', '45-0858451', true, '107', 6, 2],\n [\n false,\n 'Jabberbean',\n 'United States',\n 'Touring Tire Tube',\n '2020-04-26',\n '15-4247305',\n true,\n '15',\n 1,\n 2,\n ],\n [\n false,\n 'Thoughtblab',\n 'China',\n 'LL Bottom Bracket',\n '2020-05-21',\n '15-8534931',\n true,\n '168',\n 5,\n 2,\n ],\n [false, 'Jabbertype', 'China', 'Classic Vest', '2020-07-25', '23-1251557', true, '135', 4, 2],\n [\n false,\n 'Buzzshare',\n 'United Kingdom',\n 'Cycling Cap',\n '2020-07-07',\n '86-5920601',\n true,\n '11',\n 1,\n 4,\n ],\n [\n false,\n 'Roodel',\n 'United States',\n 'Full-Finger Gloves',\n '2020-01-13',\n '48-1055459',\n true,\n '41',\n 6,\n 4,\n ],\n [false, 'Zoovu', 'China', 'Half-Finger Gloves', '2020-06-03', '12-7842022', true, '144', 6, 1],\n [false, 'Photofeed', 'China', 'HL Mountain Frame', '2020-07-14', '94-5088099', true, '106', 1, 4],\n]\n\nexport const SELECTED_CLASS = 'selected'\nexport const ODD_ROW_CLASS = 'odd'\n", + "/src/hooksCallbacks.js": "import Handsontable from \"handsontable\";\nimport {\n SELECTED_CLASS,\n ODD_ROW_CLASS\n} from \"./constants\";\n\nconst headerAlignments = new Map([\n [\"9\", \"htCenter\"],\n [\"10\", \"htRight\"],\n [\"12\", \"htCenter\"]\n]);\n\nexport function addClassesToRows(TD, row, column, prop, value, cellProperties) {\n // Adding classes to `TR` just while rendering first visible `TD` element\n if (column !== 0) {\n return;\n }\n\n const parentElement = TD.parentElement;\n\n if (parentElement === null) {\n return;\n }\n\n // Add class to odd TRs\n if (row % 2 === 0) {\n Handsontable.dom.addClass(parentElement, ODD_ROW_CLASS);\n } else {\n Handsontable.dom.removeClass(parentElement, ODD_ROW_CLASS);\n }\n}\n\n\nexport function alignHeaders(column, TH) {\n if (column < 0) {\n return;\n }\n\n const alignmentClass = this.isRtl() ? \"htRight\" : \"htLeft\";\n\n if (TH.firstChild) {\n if (headerAlignments.has(column.toString())) {\n Handsontable.dom.removeClass(TH.firstChild, alignmentClass);\n Handsontable.dom.addClass(TH.firstChild, headerAlignments.get(column.toString()));\n } else {\n Handsontable.dom.addClass(TH.firstChild, alignmentClass);\n }\n }\n}", + "/src/styles.css": "/*\n A stylesheet customizing app (custom renderers)\n*/\n\ntable.htCore tr.odd td {\n background: #fafbff;\n}\n\n/*\n A stylesheet customizing Handsontable style\n*/\n\n.collapsibleIndicator {\n text-align: center;\n}\n\n.handsontable .green {\n background: #37bc6c;\n font-weight: bold;\n}\n\n.handsontable .orange {\n background: #fcb515;\n font-weight: bold;\n}\n\n.btn {\n padding: 20px;\n font: 1.4em sans-serif;\n}\n[data-color=\"green\"] {\n background: #37bc6c;\n}\n[data-color=\"orange\"] {\n background: #fcb515;\n}\n", + "/src/utils.js": "import { data } from './constants';\n\nconst randomName = () =>\n [\"عمر\", \"علي\", \"عبد الله\", \"معتصم\"][Math.floor(Math.random() * 3)];\nconst randomCountry = () =>\n [\"تركيا\", \"مصر\", \"لبنان\", \"العراق\"][Math.floor(Math.random() * 3)];\nconst randomDate = () =>\n new Date(Math.floor(Math.random() * Date.now())).toLocaleDateString('en-gb')\nconst randomBool = () => Math.random() > 0.5;\nconst randomNumber = (a = 0, b = 1000) => a + Math.floor(Math.random() * b);\nconst randomPhrase = () =>\n `${randomCountry()} ${randomName()} ${randomNumber()}`;\nconst randomOrderId = () =>\n `${randomNumber(100000, 999999).toString().match(/.{1,3}/g).join(\"-\")}`;\n\nfunction generateArabicData() {\n return Array.from({ length: 50 }, () => [\n randomBool(),\n randomName(),\n randomCountry(),\n randomPhrase(),\n randomDate(),\n randomOrderId(),\n randomBool(),\n randomNumber(0, 200).toString(),\n randomNumber(1, 10),\n randomNumber(1, 5),\n ]);\n}\n\nexport function isArabicDemoEnabled() {\n const urlParams = new URLSearchParams(location.search.replace(/^\\?/, \"\"));\n\n return urlParams.get(\"arabicExample\") === \"1\";\n}\n\nexport function generateExampleData() {\n return isArabicDemoEnabled() ? generateArabicData() : data;\n}\n", + "/vite.config.js": "import { defineConfig } from \"vite\";\n\nexport default defineConfig({\n base: \"./\",\n server: {\n allowedHosts: true,\n },\n});\n" + } + }, + { + "framework": "typescript", + "displayName": "TypeScript (Vite)", + "tier": 1, + "sandpackTemplate": "vanilla-ts", + "container": null, + "htWrappers": [], + "entry": "/index.ts", + "htmlEntry": "/index.html", + "devCommand": null, + "buildCommand": "tsc && vite build", + "outputDir": "dist", + "outputGlob": null, + "staticExport": false, + "spaMode": false, + "port": null, + "installCommand": "npm install", + "htCoreRange": "^18.0.0", + "fileCount": 8, + "assets": [], + "skipped": [], + "files": { + "/env.d.ts": "/// \n", + "/index.html": "\n\n \n Handsontable TypeScript Data Grid - Hello World App\n \n \n \n\n \n
\n \n \n\n", + "/index.ts": "import Handsontable from 'handsontable';\nimport 'handsontable/styles/handsontable.min.css';\nimport 'handsontable/styles/ht-theme-main.min.css';\n\nimport { data } from './src/constants';\n\nimport { alignHeaders, addClassesToRows } from './src/hooksCallbacks';\n\nconst example = document.getElementById('example1');\nif (example) {\n new Handsontable(example, {\n data,\n height: 450,\n colWidths: [170, 222, 130, 120, 120, 130, 156],\n colHeaders: [\n 'Company name',\n 'Name',\n 'Sell date',\n 'In stock',\n 'Qty',\n 'Order ID',\n 'Country',\n ],\n columns: [\n { data: 1, type: 'text' },\n { data: 3, type: 'text' },\n {\n data: 4,\n type: 'date',\n allowInvalid: false,\n },\n {\n data: 6,\n type: 'checkbox',\n className: 'htCenter',\n },\n {\n data: 7,\n type: 'numeric',\n },\n { data: 5, type: 'text' },\n { data: 2, type: 'text' },\n ],\n autoWrapRow: true,\n dropdownMenu: true,\n hiddenColumns: {\n indicators: true,\n },\n contextMenu: true,\n multiColumnSorting: true,\n filters: true,\n rowHeaders: true,\n afterGetColHeader: alignHeaders,\n beforeRenderer: addClassesToRows,\n themeName: 'ht-theme-main',\n licenseKey: 'non-commercial-and-evaluation',\n });\n\n console.log(\n `Handsontable: v${Handsontable.version} (${Handsontable.buildDate})`\n );\n} else {\n console.error(\"Element with ID 'example1' not found.\");\n}\n", + "/package.json": "{\n \"name\": \"example-typescript\",\n \"private\": true,\n \"version\": \"0.0.0\",\n \"type\": \"module\",\n \"scripts\": {\n \"dev\": \"vite --port 8080\",\n \"build\": \"tsc && vite build\",\n \"preview\": \"vite preview\"\n },\n \"devDependencies\": {\n \"typescript\": \"~6.0.3\",\n \"vite\": \"^8.1.1\"\n },\n \"dependencies\": {\n \"handsontable\": \"^18.0.0\"\n },\n \"config\": {\n \"codesandbox\": {\n \"templateId\": \"vmjhgx\"\n }\n }\n}\n", + "/src/constants.ts": "export const data = [\n [\n false,\n \"Tagcat\",\n \"United Kingdom\",\n \"Classic Vest\",\n \"2020-10-11\",\n \"01-2331942\",\n true,\n \"172\",\n 2,\n 2\n ],\n [\n true,\n \"Zoomzone\",\n \"Indonesia\",\n \"Cycling Cap\",\n \"2020-05-03\",\n \"88-2768633\",\n true,\n \"188\",\n 6,\n 2\n ],\n [\n true,\n \"Meeveo\",\n \"United States\",\n \"Full-Finger Gloves\",\n \"2020-03-27\",\n \"51-6775945\",\n true,\n \"162\",\n 1,\n 3\n ],\n [\n false,\n \"Buzzdog\",\n \"Philippines\",\n \"HL Mountain Frame\",\n \"2020-08-29\",\n \"44-4028109\",\n true,\n \"133\",\n 7,\n 1\n ],\n [\n true,\n \"Katz\",\n \"India\",\n \"Half-Finger Gloves\",\n \"2020-10-02\",\n \"08-2758492\",\n true,\n \"87\",\n 1,\n 3\n ],\n [\n false,\n \"Jaxbean\",\n \"China\",\n \"HL Road Frame\",\n \"2020-09-28\",\n \"84-3557705\",\n false,\n \"26\",\n 8,\n 1\n ],\n [\n false,\n \"Wikido\",\n \"Brazil\",\n \"HL Touring Frame\",\n \"2020-06-24\",\n \"20-9397637\",\n false,\n \"110\",\n 4,\n 1\n ],\n [\n false,\n \"Browsedrive\",\n \"United States\",\n \"LL Mountain Frame\",\n \"2020-03-13\",\n \"36-0079556\",\n true,\n \"50\",\n 4,\n 4\n ],\n [\n false,\n \"Twinder\",\n \"United Kingdom\",\n \"LL Road Frame\",\n \"2020-04-06\",\n \"41-1489542\",\n false,\n \"160\",\n 6,\n 1\n ],\n [\n false,\n \"Jetwire\",\n \"China\",\n \"LL Touring Frame\",\n \"2020-02-01\",\n \"37-1531629\",\n true,\n \"30\",\n 8,\n 5\n ],\n [\n false,\n \"Chatterpoint\",\n \"China\",\n \"Long-Sleeve Logo Jersey\",\n \"2020-07-14\",\n \"25-5083429\",\n true,\n \"39\",\n 7,\n 2\n ],\n [\n false,\n \"Twinder\",\n \"Egypt\",\n \"Men's Bib-Shorts\",\n \"2020-08-31\",\n \"04-4281278\",\n false,\n \"96\",\n 6,\n 1\n ],\n [\n false,\n \"Midel\",\n \"United States\",\n \"Men's Sports Shorts\",\n \"2020-06-27\",\n \"55-1711908\",\n true,\n \"108\",\n 10,\n 3\n ],\n [\n false,\n \"Yodo\",\n \"India\",\n \"ML Mountain Frame\",\n \"2020-03-16\",\n \"58-8360815\",\n false,\n \"46\",\n 1,\n 1\n ],\n [\n false,\n \"Camido\",\n \"Russia\",\n \"ML Mountain Frame-W\",\n \"2020-09-13\",\n \"10-3786104\",\n true,\n \"97\",\n 8,\n 3\n ],\n [\n false,\n \"Eire\",\n \"Thailand\",\n \"ML Road Frame\",\n \"2020-04-10\",\n \"45-1186054\",\n true,\n \"161\",\n 1,\n 4\n ],\n [\n false,\n \"Vinte\",\n \"United Kingdom\",\n \"ML Road Frame-W\",\n \"2020-01-22\",\n \"62-6202742\",\n true,\n \"58\",\n 4,\n 3\n ],\n [\n false,\n \"Twitterlist\",\n \"China\",\n \"Mountain Bike Socks\",\n \"2020-11-09\",\n \"88-9646223\",\n true,\n \"92\",\n 8,\n 3\n ],\n [\n false,\n \"Eidel\",\n \"Bangladesh\",\n \"Mountain-100\",\n \"2020-09-19\",\n \"45-5588112\",\n true,\n \"5\",\n 6,\n 5\n ],\n [\n false,\n \"Trunyx\",\n \"Nigeria\",\n \"Mountain-200\",\n \"2020-03-09\",\n \"66-6271819\",\n true,\n \"158\",\n 4,\n 1\n ],\n [\n false,\n \"Katz\",\n \"Turkey\",\n \"Mountain-300\",\n \"2020-03-05\",\n \"38-9245023\",\n false,\n \"121\",\n 5,\n 4\n ],\n [\n false,\n \"Kaymbo\",\n \"United States\",\n \"Mountain-400-W\",\n \"2020-12-24\",\n \"44-5916927\",\n false,\n \"61\",\n 5,\n 4\n ],\n [\n false,\n \"Ozu\",\n \"Pakistan\",\n \"Mountain-500\",\n \"2020-06-13\",\n \"31-5449914\",\n true,\n \"155\",\n 2,\n 2\n ],\n [\n false,\n \"Rhynyx\",\n \"India\",\n \"Racing Socks\",\n \"2020-12-05\",\n \"19-9413869\",\n true,\n \"162\",\n 2,\n 4\n ],\n [\n false,\n \"Flashset\",\n \"Iran\",\n \"Road-150\",\n \"2020-12-14\",\n \"25-9807605\",\n false,\n \"46\",\n 7,\n 1\n ],\n [\n false,\n \"Yata\",\n \"Congo (Kinshasa)\",\n \"Road-250\",\n \"2020-06-12\",\n \"74-4291983\",\n true,\n \"47\",\n 4,\n 4\n ],\n [\n false,\n \"Brainlounge\",\n \"Vietnam\",\n \"Road-350-W\",\n \"2020-03-10\",\n \"83-0980643\",\n true,\n \"104\",\n 2,\n 3\n ],\n [\n false,\n \"Babblestorm\",\n \"United States\",\n \"Road-450\",\n \"2020-10-10\",\n \"19-2878430\",\n true,\n \"101\",\n 6,\n 4\n ],\n [\n false,\n \"Youspan\",\n \"Brazil\",\n \"Road-550-W\",\n \"2020-12-16\",\n \"19-1838230\",\n true,\n \"150\",\n 10,\n 3\n ],\n [\n false,\n \"Nlounge\",\n \"China\",\n \"Road-650\",\n \"2020-10-31\",\n \"32-2267938\",\n true,\n \"42\",\n 4,\n 2\n ],\n [\n false,\n \"Twinte\",\n \"India\",\n \"Road-750\",\n \"2020-08-17\",\n \"79-2821972\",\n true,\n \"144\",\n 9,\n 3\n ],\n [\n false,\n \"Oyonder\",\n \"United Kingdom\",\n \"Short-Sleeve Classic Jersey\",\n \"2020-12-04\",\n \"46-6597557\",\n true,\n \"195\",\n 4,\n 1\n ],\n [\n false,\n \"Gigabox\",\n \"Pakistan\",\n \"Sport-100\",\n \"2020-02-03\",\n \"15-1793960\",\n true,\n \"199\",\n 4,\n 4\n ],\n [\n false,\n \"Livetube\",\n \"France\",\n \"Touring-1000\",\n \"2020-05-16\",\n \"86-0811003\",\n true,\n \"110\",\n 4,\n 5\n ],\n [\n false,\n \"Voomm\",\n \"United Kingdom\",\n \"Touring-2000\",\n \"2020-07-15\",\n \"95-3068680\",\n true,\n \"51\",\n 4,\n 4\n ],\n [\n false,\n \"Voonyx\",\n \"China\",\n \"Touring-3000\",\n \"2020-11-27\",\n \"35-3085360\",\n false,\n \"69\",\n 2,\n 5\n ],\n [\n false,\n \"Zoombeat\",\n \"United States\",\n \"Women's Mountain Shorts\",\n \"2020-11-03\",\n \"56-8673088\",\n false,\n \"53\",\n 2,\n 3\n ],\n [\n false,\n \"Roomm\",\n \"China\",\n \"Women's Tights\",\n \"2020-03-16\",\n \"76-0085918\",\n true,\n \"168\",\n 1,\n 1\n ],\n [\n false,\n \"Leenti\",\n \"China\",\n \"Mountain-400\",\n \"2020-05-16\",\n \"03-0893276\",\n false,\n \"58\",\n 1,\n 4\n ],\n [\n false,\n \"Jetpulse\",\n \"United States\",\n \"Road-550\",\n \"2020-02-08\",\n \"79-9013306\",\n true,\n \"152\",\n 9,\n 3\n ],\n [\n false,\n \"Katz\",\n \"Peru\",\n \"Road-350\",\n \"2020-02-15\",\n \"55-7799920\",\n true,\n \"66\",\n 4,\n 2\n ],\n [\n false,\n \"Cogidoo\",\n \"India\",\n \"LL Mountain Front Wheel\",\n \"2020-06-04\",\n \"07-0881122\",\n false,\n \"112\",\n 9,\n 2\n ],\n [\n false,\n \"Divavu\",\n \"Colombia\",\n \"Touring Rear Wheel\",\n \"2020-02-24\",\n \"58-6157387\",\n true,\n \"50\",\n 10,\n 4\n ],\n [\n false,\n \"Mydeo\",\n \"China\",\n \"Touring Front Wheel\",\n \"2020-12-07\",\n \"12-2810010\",\n false,\n \"31\",\n 3,\n 5\n ],\n [\n false,\n \"Browsebug\",\n \"Japan\",\n \"ML Mountain Front Wheel\",\n \"2020-01-14\",\n \"64-9249984\",\n true,\n \"132\",\n 5,\n 5\n ],\n [\n false,\n \"Layo\",\n \"China\",\n \"HL Mountain Front Wheel\",\n \"2020-04-24\",\n \"45-0739652\",\n true,\n \"45\",\n 1,\n 5\n ],\n [\n false,\n \"Snaptags\",\n \"United Kingdom\",\n \"LL Touring Handlebars\",\n \"2020-08-06\",\n \"09-5712761\",\n true,\n \"197\",\n 4,\n 2\n ],\n [\n false,\n \"Cogilith\",\n \"China\",\n \"HL Touring Handlebars\",\n \"2020-05-31\",\n \"01-7345008\",\n true,\n \"190\",\n 4,\n 3\n ],\n [\n false,\n \"Reallinks\",\n \"United Kingdom\",\n \"LL Road Front Wheel\",\n \"2020-05-14\",\n \"62-1065350\",\n true,\n \"184\",\n 3,\n 4\n ],\n [\n false,\n \"Quaxo\",\n \"United States\",\n \"ML Road Front Wheel\",\n \"2020-03-23\",\n \"44-7241323\",\n true,\n \"169\",\n 3,\n 4\n ],\n [\n false,\n \"Devify\",\n \"China\",\n \"HL Road Front Wheel\",\n \"2020-12-12\",\n \"52-0295699\",\n false,\n \"152\",\n 4,\n 4\n ],\n [\n false,\n \"Youopia\",\n \"Angola\",\n \"LL Mountain Handlebars\",\n \"2020-04-01\",\n \"52-2650922\",\n false,\n \"182\",\n 6,\n 4\n ],\n [\n false,\n \"Ainyx\",\n \"China\",\n \"Touring Pedal\",\n \"2020-02-27\",\n \"48-3618525\",\n true,\n \"141\",\n 6,\n 1\n ],\n [\n false,\n \"Browsetype\",\n \"Malaysia\",\n \"ML Mountain Handlebars\",\n \"2020-04-28\",\n \"51-8893923\",\n true,\n \"169\",\n 7,\n 1\n ],\n [\n false,\n \"Muxo\",\n \"China\",\n \"HL Mountain Handlebars\",\n \"2020-08-22\",\n \"68-5911361\",\n false,\n \"39\",\n 7,\n 1\n ],\n [\n false,\n \"Bubbletube\",\n \"China\",\n \"LL Road Handlebars\",\n \"2020-10-04\",\n \"41-5880042\",\n true,\n \"71\",\n 8,\n 3\n ],\n [\n false,\n \"Fadeo\",\n \"Vietnam\",\n \"ML Road Handlebars\",\n \"2020-04-23\",\n \"90-5913983\",\n true,\n \"148\",\n 10,\n 3\n ],\n [\n false,\n \"Yadel\",\n \"United Kingdom\",\n \"HL Road Handlebars\",\n \"2020-04-18\",\n \"92-0960699\",\n true,\n \"116\",\n 8,\n 1\n ],\n [\n false,\n \"Blognation\",\n \"China\",\n \"LL Headset\",\n \"2020-01-10\",\n \"06-9493898\",\n true,\n \"96\",\n 10,\n 1\n ],\n [\n false,\n \"Devpoint\",\n \"China\",\n \"ML Headset\",\n \"2020-12-25\",\n \"69-5878565\",\n true,\n \"35\",\n 4,\n 2\n ],\n [\n false,\n \"Aibox\",\n \"United Kingdom\",\n \"HL Headset\",\n \"2020-03-18\",\n \"13-1133017\",\n true,\n \"16\",\n 8,\n 2\n ],\n [\n false,\n \"Brightdog\",\n \"China\",\n \"LL Mountain Pedal\",\n \"2020-09-11\",\n \"39-6530433\",\n true,\n \"194\",\n 2,\n 5\n ],\n [\n false,\n \"Gabcube\",\n \"Nigeria\",\n \"ML Mountain Pedal\",\n \"2020-04-22\",\n \"96-6860388\",\n true,\n \"24\",\n 1,\n 3\n ],\n [\n false,\n \"Muxo\",\n \"China\",\n \"HL Mountain Pedal\",\n \"2020-06-05\",\n \"30-0356137\",\n true,\n \"170\",\n 4,\n 4\n ],\n [\n false,\n \"Tambee\",\n \"China\",\n \"ML Touring Seat/Saddle\",\n \"2020-02-22\",\n \"93-9058255\",\n true,\n \"184\",\n 9,\n 5\n ],\n [\n false,\n \"Cogilith\",\n \"India\",\n \"LL Touring Seat/Saddle\",\n \"2020-04-06\",\n \"82-9268909\",\n false,\n \"153\",\n 10,\n 4\n ],\n [\n false,\n \"Dynabox\",\n \"Hong Kong\",\n \"HL Touring Seat/Saddle\",\n \"2020-01-10\",\n \"20-6913815\",\n false,\n \"88\",\n 10,\n 1\n ],\n [\n false,\n \"Shuffledrive\",\n \"Sudan\",\n \"LL Road Pedal\",\n \"2020-09-16\",\n \"08-8238817\",\n true,\n \"57\",\n 9,\n 2\n ],\n [\n false,\n \"Fivechat\",\n \"China\",\n \"ML Road Pedal\",\n \"2020-08-26\",\n \"44-7370350\",\n false,\n \"62\",\n 4,\n 1\n ],\n [\n false,\n \"Meembee\",\n \"United States\",\n \"HL Road Pedal\",\n \"2020-12-27\",\n \"01-3525949\",\n true,\n \"123\",\n 2,\n 4\n ],\n [\n false,\n \"Dynazzy\",\n \"United Kingdom\",\n \"LL Mountain Seat/Saddle 1\",\n \"2020-12-15\",\n \"04-2414623\",\n true,\n \"77\",\n 10,\n 5\n ],\n [\n false,\n \"Eare\",\n \"China\",\n \"ML Mountain Seat/Saddle 1\",\n \"2020-04-04\",\n \"15-1917509\",\n false,\n \"199\",\n 9,\n 4\n ],\n [\n false,\n \"Yozio\",\n \"China\",\n \"HL Mountain Seat/Saddle 1\",\n \"2020-03-15\",\n \"06-2526845\",\n true,\n \"149\",\n 8,\n 2\n ],\n [\n false,\n \"Quinu\",\n \"Xi'an\",\n \"425-777-7829\",\n \"2020-02-22\",\n \"83-1713558\",\n false,\n \"191\",\n 9,\n 5\n ],\n [\n false,\n \"Jazzy\",\n \"United Kingdom\",\n \"ML Road Seat/Saddle 1\",\n \"2020-08-07\",\n \"00-8892524\",\n true,\n \"150\",\n 10,\n 2\n ],\n [\n false,\n \"Thoughtsphere\",\n \"China\",\n \"HL Road Seat/Saddle 1\",\n \"2020-11-28\",\n \"39-5538991\",\n true,\n \"130\",\n 7,\n 3\n ],\n [\n false,\n \"Leenti\",\n \"China\",\n \"ML Road Rear Wheel\",\n \"2020-12-29\",\n \"06-9002973\",\n true,\n \"179\",\n 1,\n 2\n ],\n [\n false,\n \"Quaxo\",\n \"United Kingdom\",\n \"HL Road Rear Wheel\",\n \"2020-09-06\",\n \"73-6104901\",\n true,\n \"98\",\n 5,\n 3\n ],\n [\n false,\n \"Tanoodle\",\n \"Chile\",\n \"LL Mountain Seat/Saddle 2\",\n \"2020-05-24\",\n \"68-7384479\",\n true,\n \"175\",\n 2,\n 3\n ],\n [\n false,\n \"Feednation\",\n \"China\",\n \"ML Mountain Seat/Saddle 2\",\n \"2020-11-21\",\n \"26-7757763\",\n true,\n \"11\",\n 1,\n 3\n ],\n [\n false,\n \"Kayveo\",\n \"China\",\n \"HL Mountain Seat/Saddle 2\",\n \"2020-06-21\",\n \"07-4873562\",\n false,\n \"184\",\n 7,\n 4\n ],\n [\n false,\n \"Meevee\",\n \"Saudi Arabia\",\n \"LL Road Seat/Saddle 1\",\n \"2020-11-16\",\n \"46-5819554\",\n false,\n \"27\",\n 9,\n 3\n ],\n [\n false,\n \"Twitterworks\",\n \"China\",\n \"ML Road Seat/Saddle 2\",\n \"2020-04-19\",\n \"01-2666826\",\n true,\n \"186\",\n 3,\n 2\n ],\n [\n false,\n \"Wikizz\",\n \"Tanzania\",\n \"HL Road Seat/Saddle 2\",\n \"2020-03-08\",\n \"54-7090503\",\n true,\n \"20\",\n 3,\n 3\n ],\n [\n false,\n \"Yoveo\",\n \"United States\",\n \"LL Mountain Tire\",\n \"2020-10-14\",\n \"78-7658520\",\n false,\n \"153\",\n 2,\n 1\n ],\n [\n false,\n \"Yakidoo\",\n \"China\",\n \"ML Mountain Tire\",\n \"2020-10-12\",\n \"23-9926318\",\n true,\n \"161\",\n 8,\n 5\n ],\n [\n false,\n \"Oyope\",\n \"China\",\n \"HL Mountain Tire\",\n \"2020-09-20\",\n \"20-0179517\",\n true,\n \"98\",\n 10,\n 5\n ],\n [\n false,\n \"Skipstorm\",\n \"United States\",\n \"LL Road Tire\",\n \"2020-10-01\",\n \"02-9543343\",\n true,\n \"30\",\n 7,\n 3\n ],\n [\n false,\n \"Minyx\",\n \"United States\",\n \"ML Road Tire\",\n \"2020-07-07\",\n \"98-3938169\",\n true,\n \"73\",\n 10,\n 2\n ],\n [\n false,\n \"Miboo\",\n \"China\",\n \"HL Road Tire\",\n \"2020-07-25\",\n \"68-5197934\",\n true,\n \"158\",\n 9,\n 1\n ],\n [\n false,\n \"Realfire\",\n \"United States\",\n \"Touring Tire\",\n \"2020-08-27\",\n \"39-8260460\",\n true,\n \"122\",\n 5,\n 2\n ],\n [\n false,\n \"Shufflester\",\n \"China\",\n \"Mountain Tire Tube\",\n \"2020-06-08\",\n \"45-9776170\",\n true,\n \"33\",\n 2,\n 4\n ],\n [\n false,\n \"Ntag\",\n \"China\",\n \"Road Tire Tube\",\n \"2020-12-06\",\n \"45-0858451\",\n true,\n \"107\",\n 6,\n 2\n ],\n [\n false,\n \"Jabberbean\",\n \"United States\",\n \"Touring Tire Tube\",\n \"2020-04-26\",\n \"15-4247305\",\n true,\n \"15\",\n 1,\n 2\n ],\n [\n false,\n \"Thoughtblab\",\n \"China\",\n \"LL Bottom Bracket\",\n \"2020-05-21\",\n \"15-8534931\",\n true,\n \"168\",\n 5,\n 2\n ],\n [\n false,\n \"Jabbertype\",\n \"China\",\n \"Classic Vest\",\n \"2020-07-25\",\n \"23-1251557\",\n true,\n \"135\",\n 4,\n 2\n ],\n [\n false,\n \"Buzzshare\",\n \"United Kingdom\",\n \"Cycling Cap\",\n \"2020-07-07\",\n \"86-5920601\",\n true,\n \"11\",\n 1,\n 4\n ],\n [\n false,\n \"Roodel\",\n \"United States\",\n \"Full-Finger Gloves\",\n \"2020-01-13\",\n \"48-1055459\",\n true,\n \"41\",\n 6,\n 4\n ],\n [\n false,\n \"Zoovu\",\n \"China\",\n \"Half-Finger Gloves\",\n \"2020-06-03\",\n \"12-7842022\",\n true,\n \"144\",\n 6,\n 1\n ],\n [\n false,\n \"Photofeed\",\n \"China\",\n \"HL Mountain Frame\",\n \"2020-07-14\",\n \"94-5088099\",\n true,\n \"106\",\n 1,\n 4\n ]\n];\n\nexport const SELECTED_CLASS = 'selected'\nexport const ODD_ROW_CLASS = \"odd\";\n", + "/src/hooksCallbacks.ts": "import Handsontable from \"handsontable\";\nimport {\n SELECTED_CLASS,\n ODD_ROW_CLASS\n} from \"./constants\";\n\nconst headerAlignments = new Map([\n [\"9\", \"htCenter\"],\n [\"10\", \"htRight\"],\n [\"12\", \"htCenter\"]\n]);\n\ntype AddClassesToRows = (\n TD: HTMLTableCellElement,\n row: number,\n column: number,\n prop: number | string,\n value: any,\n cellProperties: Handsontable.CellProperties\n) => void;\n\nexport const addClassesToRows: AddClassesToRows = (\n TD,\n row,\n column,\n _prop,\n _value,\n _cellProperties\n) => {\n // Adding classes to `TR` just while rendering first visible `TD` element\n if (column !== 0) {\n return;\n }\n\n const parentElement = TD.parentElement;\n\n if (parentElement === null) {\n return;\n }\n\n // Add class to odd TRs\n if (row % 2 === 0) {\n Handsontable.dom.addClass(parentElement, ODD_ROW_CLASS);\n } else {\n Handsontable.dom.removeClass(parentElement, ODD_ROW_CLASS);\n }\n};\n\nexport function alignHeaders(this: Handsontable, column: number, TH: HTMLTableCellElement) {\n if (column < 0) {\n return;\n }\n\n const alignmentClass = this.isRtl() ? \"htRight\" : \"htLeft\";\n\n if (TH.firstChild) {\n if (headerAlignments.has(column.toString())) {\n Handsontable.dom.removeClass(TH.firstChild as HTMLElement, alignmentClass);\n Handsontable.dom.addClass(TH.firstChild as HTMLElement, headerAlignments.get(column.toString()) as string);\n } else {\n Handsontable.dom.addClass(TH.firstChild as HTMLElement, alignmentClass);\n }\n }\n};\n", + "/src/styles.css": "/*\n A stylesheet customizing app (custom renderers)\n*/\n\ntable.htCore tr.odd td {\n background: #fafbff;\n}\n\n/*\n A stylesheet customizing Handsontable style\n*/\n\n.handsontable {\n font-size: 13px;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", \"Roboto\", \"Oxygen\",\n \"Ubuntu\", \"Helvetica Neue\", Arial, sans-serif;\n font-weight: 400;\n}\n\n.collapsibleIndicator {\n text-align: center;\n}\n", + "/tsconfig.json": "{\n \"compilerOptions\": {\n \"target\": \"es2023\",\n \"module\": \"esnext\",\n \"lib\": [\"ES2023\", \"DOM\"],\n \"types\": [\"vite/client\"],\n \"skipLibCheck\": true,\n\n /* Bundler mode */\n \"moduleResolution\": \"bundler\",\n \"allowImportingTsExtensions\": true,\n \"verbatimModuleSyntax\": true,\n \"moduleDetection\": \"force\",\n \"noEmit\": true,\n\n /* Linting */\n \"noUnusedLocals\": true,\n \"noUnusedParameters\": true,\n \"erasableSyntaxOnly\": true,\n \"noFallthroughCasesInSwitch\": true\n },\n \"include\": [\"src\"]\n}\n" + } + }, + { + "framework": "react", + "displayName": "React (Vite, TS)", + "tier": 1, + "sandpackTemplate": "react-ts", + "container": null, + "htWrappers": [ + "@handsontable/react-wrapper" + ], + "entry": "/src/index.tsx", + "htmlEntry": "/index.html", + "devCommand": null, + "buildCommand": "tsc -b && vite build", + "outputDir": "dist", + "outputGlob": null, + "staticExport": false, + "spaMode": false, + "port": null, + "installCommand": "npm install", + "htCoreRange": "^18.0.0", + "fileCount": 15, + "assets": [], + "skipped": [], + "files": { + "/.gitignore": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\nlerna-debug.log*\n\nnode_modules\ndist\ndist-ssr\n*.local\n\n# Editor directories and files\n.vscode/*\n!.vscode/extensions.json\n.idea\n.DS_Store\n*.suo\n*.ntvs*\n*.njsproj\n*.sln\n*.sw?\n", + "/.npmrc": "legacy-peer-deps=true\n", + "/README.md": "# Demo\n\n## Description\n\nA general purpose demo showcasing some of the most popular features of Handsontable in React.\n\n## Installation\n\nInstall all necessary dependencies from npm with `npm install`.\n\n## Development\n\nTo start local development server use `npm run dev`. You can then visit http://localhost:5173 to view the project.\n\n## Testing\n\nTo run tests for this project, first launch the development server with `npm run dev`. Once the server is running, execute `npm run test` to run the test specs.\n\n## Forking\n\nThere are two ways to fork this project in Handsontable's monorepo:\n\n1. Fork the entire repository.\n2. Copy the example to a new repository. Detailed instructions are available [here](../../../../README.md#copying-an-example-to-a-separate-repo) in the section _Copying an example to a separate repo_.\n\n## License\n\nHandsontable is a commercial software with two licenses available:\n\n- Free for non-commercial purposes such as teaching, academic research, and evaluation. [Read it here](https://github.com/handsontable/handsontable/blob/master/handsontable-non-commercial-license.pdf).\n- Commercial license with support and maintenance included. See [pricing plans](https://handsontable.com/pricing).\n\n## Get help\n\nJoin our [community](https://github.com/handsontable/handsontable/discussions) to get help, share ideas, and contribute to the ongoing development of Handsontable!\n\nCommercial license holders with an active support plan can contact our [Support Team](https://handsontable.com/contact?category=technical_support).\n", + "/eslint.config.js": "import js from '@eslint/js'\nimport globals from 'globals'\nimport reactHooks from 'eslint-plugin-react-hooks'\nimport reactRefresh from 'eslint-plugin-react-refresh'\nimport tseslint from 'typescript-eslint'\nimport { defineConfig, globalIgnores } from 'eslint/config'\n\nexport default defineConfig([\n globalIgnores(['dist']),\n {\n files: ['**/*.{ts,tsx}'],\n extends: [\n js.configs.recommended,\n tseslint.configs.recommended,\n reactHooks.configs.flat.recommended,\n reactRefresh.configs.vite,\n ],\n languageOptions: {\n globals: globals.browser,\n },\n },\n])\n", + "/index.html": "\n\n \n \n \n \n Handsontable for React example\n \n \n
\n \n \n\n", + "/package.json": "{\n \"name\": \"example-react-typescript\",\n \"private\": true,\n \"version\": \"0.0.0\",\n \"type\": \"module\",\n \"scripts\": {\n \"dev\": \"vite --port 8080\",\n \"build\": \"tsc -b && vite build\",\n \"lint\": \"eslint .\",\n \"preview\": \"vite preview\"\n },\n \"dependencies\": {\n \"@handsontable/react-wrapper\": \"^18.0.0\",\n \"handsontable\": \"^18.0.0\",\n \"react\": \"^19.2.7\",\n \"react-dom\": \"^19.2.6\"\n },\n \"devDependencies\": {\n \"@eslint/js\": \"^10.0.1\",\n \"@types/node\": \"^24.13.2\",\n \"@types/react\": \"^19.2.17\",\n \"@types/react-dom\": \"^19.2.3\",\n \"@vitejs/plugin-react\": \"^6.0.3\",\n \"eslint\": \"^10.6.0\",\n \"eslint-plugin-react-hooks\": \"^7.1.1\",\n \"eslint-plugin-react-refresh\": \"^0.5.3\",\n \"globals\": \"^17.7.0\",\n \"typescript\": \"~6.0.3\",\n \"typescript-eslint\": \"^8.62.1\",\n \"vite\": \"^8.1.1\"\n },\n \"config\": {\n \"codesandbox\": {\n \"templateId\": \"vmjhgx\"\n }\n }\n}\n", + "/src/constants.ts": "export const data = [\n [\n false,\n \"Tagcat\",\n \"United Kingdom\",\n \"Classic Vest\",\n \"2020-10-11\",\n \"01-2331942\",\n true,\n \"172\",\n 2,\n 2\n ],\n [\n true,\n \"Zoomzone\",\n \"Indonesia\",\n \"Cycling Cap\",\n \"2020-05-03\",\n \"88-2768633\",\n true,\n \"188\",\n 6,\n 2\n ],\n [\n true,\n \"Meeveo\",\n \"United States\",\n \"Full-Finger Gloves\",\n \"2020-03-27\",\n \"51-6775945\",\n true,\n \"162\",\n 1,\n 3\n ],\n [\n false,\n \"Buzzdog\",\n \"Philippines\",\n \"HL Mountain Frame\",\n \"2020-08-29\",\n \"44-4028109\",\n true,\n \"133\",\n 7,\n 1\n ],\n [\n true,\n \"Katz\",\n \"India\",\n \"Half-Finger Gloves\",\n \"2020-10-02\",\n \"08-2758492\",\n true,\n \"87\",\n 1,\n 3\n ],\n [\n false,\n \"Jaxbean\",\n \"China\",\n \"HL Road Frame\",\n \"2020-09-28\",\n \"84-3557705\",\n false,\n \"26\",\n 8,\n 1\n ],\n [\n false,\n \"Wikido\",\n \"Brazil\",\n \"HL Touring Frame\",\n \"2020-06-24\",\n \"20-9397637\",\n false,\n \"110\",\n 4,\n 1\n ],\n [\n false,\n \"Browsedrive\",\n \"United States\",\n \"LL Mountain Frame\",\n \"2020-03-13\",\n \"36-0079556\",\n true,\n \"50\",\n 4,\n 4\n ],\n [\n false,\n \"Twinder\",\n \"United Kingdom\",\n \"LL Road Frame\",\n \"2020-04-06\",\n \"41-1489542\",\n false,\n \"160\",\n 6,\n 1\n ],\n [\n false,\n \"Jetwire\",\n \"China\",\n \"LL Touring Frame\",\n \"2020-02-01\",\n \"37-1531629\",\n true,\n \"30\",\n 8,\n 5\n ],\n [\n false,\n \"Chatterpoint\",\n \"China\",\n \"Long-Sleeve Logo Jersey\",\n \"2020-07-14\",\n \"25-5083429\",\n true,\n \"39\",\n 7,\n 2\n ],\n [\n false,\n \"Twinder\",\n \"Egypt\",\n \"Men's Bib-Shorts\",\n \"2020-08-31\",\n \"04-4281278\",\n false,\n \"96\",\n 6,\n 1\n ],\n [\n false,\n \"Midel\",\n \"United States\",\n \"Men's Sports Shorts\",\n \"2020-06-27\",\n \"55-1711908\",\n true,\n \"108\",\n 10,\n 3\n ],\n [\n false,\n \"Yodo\",\n \"India\",\n \"ML Mountain Frame\",\n \"2020-03-16\",\n \"58-8360815\",\n false,\n \"46\",\n 1,\n 1\n ],\n [\n false,\n \"Camido\",\n \"Russia\",\n \"ML Mountain Frame-W\",\n \"2020-09-13\",\n \"10-3786104\",\n true,\n \"97\",\n 8,\n 3\n ],\n [\n false,\n \"Eire\",\n \"Thailand\",\n \"ML Road Frame\",\n \"2020-04-10\",\n \"45-1186054\",\n true,\n \"161\",\n 1,\n 4\n ],\n [\n false,\n \"Vinte\",\n \"United Kingdom\",\n \"ML Road Frame-W\",\n \"2020-01-22\",\n \"62-6202742\",\n true,\n \"58\",\n 4,\n 3\n ],\n [\n false,\n \"Twitterlist\",\n \"China\",\n \"Mountain Bike Socks\",\n \"2020-11-09\",\n \"88-9646223\",\n true,\n \"92\",\n 8,\n 3\n ],\n [\n false,\n \"Eidel\",\n \"Bangladesh\",\n \"Mountain-100\",\n \"2020-09-19\",\n \"45-5588112\",\n true,\n \"5\",\n 6,\n 5\n ],\n [\n false,\n \"Trunyx\",\n \"Nigeria\",\n \"Mountain-200\",\n \"2020-03-09\",\n \"66-6271819\",\n true,\n \"158\",\n 4,\n 1\n ],\n [\n false,\n \"Katz\",\n \"Turkey\",\n \"Mountain-300\",\n \"2020-03-05\",\n \"38-9245023\",\n false,\n \"121\",\n 5,\n 4\n ],\n [\n false,\n \"Kaymbo\",\n \"United States\",\n \"Mountain-400-W\",\n \"2020-12-24\",\n \"44-5916927\",\n false,\n \"61\",\n 5,\n 4\n ],\n [\n false,\n \"Ozu\",\n \"Pakistan\",\n \"Mountain-500\",\n \"2020-06-13\",\n \"31-5449914\",\n true,\n \"155\",\n 2,\n 2\n ],\n [\n false,\n \"Rhynyx\",\n \"India\",\n \"Racing Socks\",\n \"2020-12-05\",\n \"19-9413869\",\n true,\n \"162\",\n 2,\n 4\n ],\n [\n false,\n \"Flashset\",\n \"Iran\",\n \"Road-150\",\n \"2020-12-14\",\n \"25-9807605\",\n false,\n \"46\",\n 7,\n 1\n ],\n [\n false,\n \"Yata\",\n \"Congo (Kinshasa)\",\n \"Road-250\",\n \"2020-06-12\",\n \"74-4291983\",\n true,\n \"47\",\n 4,\n 4\n ],\n [\n false,\n \"Brainlounge\",\n \"Vietnam\",\n \"Road-350-W\",\n \"2020-03-10\",\n \"83-0980643\",\n true,\n \"104\",\n 2,\n 3\n ],\n [\n false,\n \"Babblestorm\",\n \"United States\",\n \"Road-450\",\n \"2020-10-10\",\n \"19-2878430\",\n true,\n \"101\",\n 6,\n 4\n ],\n [\n false,\n \"Youspan\",\n \"Brazil\",\n \"Road-550-W\",\n \"2020-12-16\",\n \"19-1838230\",\n true,\n \"150\",\n 10,\n 3\n ],\n [\n false,\n \"Nlounge\",\n \"China\",\n \"Road-650\",\n \"2020-10-31\",\n \"32-2267938\",\n true,\n \"42\",\n 4,\n 2\n ],\n [\n false,\n \"Twinte\",\n \"India\",\n \"Road-750\",\n \"2020-08-17\",\n \"79-2821972\",\n true,\n \"144\",\n 9,\n 3\n ],\n [\n false,\n \"Oyonder\",\n \"United Kingdom\",\n \"Short-Sleeve Classic Jersey\",\n \"2020-12-04\",\n \"46-6597557\",\n true,\n \"195\",\n 4,\n 1\n ],\n [\n false,\n \"Gigabox\",\n \"Pakistan\",\n \"Sport-100\",\n \"2020-02-03\",\n \"15-1793960\",\n true,\n \"199\",\n 4,\n 4\n ],\n [\n false,\n \"Livetube\",\n \"France\",\n \"Touring-1000\",\n \"2020-05-16\",\n \"86-0811003\",\n true,\n \"110\",\n 4,\n 5\n ],\n [\n false,\n \"Voomm\",\n \"United Kingdom\",\n \"Touring-2000\",\n \"2020-07-15\",\n \"95-3068680\",\n true,\n \"51\",\n 4,\n 4\n ],\n [\n false,\n \"Voonyx\",\n \"China\",\n \"Touring-3000\",\n \"2020-11-27\",\n \"35-3085360\",\n false,\n \"69\",\n 2,\n 5\n ],\n [\n false,\n \"Zoombeat\",\n \"United States\",\n \"Women's Mountain Shorts\",\n \"2020-11-03\",\n \"56-8673088\",\n false,\n \"53\",\n 2,\n 3\n ],\n [\n false,\n \"Roomm\",\n \"China\",\n \"Women's Tights\",\n \"2020-03-16\",\n \"76-0085918\",\n true,\n \"168\",\n 1,\n 1\n ],\n [\n false,\n \"Leenti\",\n \"China\",\n \"Mountain-400\",\n \"2020-05-16\",\n \"03-0893276\",\n false,\n \"58\",\n 1,\n 4\n ],\n [\n false,\n \"Jetpulse\",\n \"United States\",\n \"Road-550\",\n \"2020-02-08\",\n \"79-9013306\",\n true,\n \"152\",\n 9,\n 3\n ],\n [\n false,\n \"Katz\",\n \"Peru\",\n \"Road-350\",\n \"2020-02-15\",\n \"55-7799920\",\n true,\n \"66\",\n 4,\n 2\n ],\n [\n false,\n \"Cogidoo\",\n \"India\",\n \"LL Mountain Front Wheel\",\n \"2020-06-04\",\n \"07-0881122\",\n false,\n \"112\",\n 9,\n 2\n ],\n [\n false,\n \"Divavu\",\n \"Colombia\",\n \"Touring Rear Wheel\",\n \"2020-02-24\",\n \"58-6157387\",\n true,\n \"50\",\n 10,\n 4\n ],\n [\n false,\n \"Mydeo\",\n \"China\",\n \"Touring Front Wheel\",\n \"2020-12-07\",\n \"12-2810010\",\n false,\n \"31\",\n 3,\n 5\n ],\n [\n false,\n \"Browsebug\",\n \"Japan\",\n \"ML Mountain Front Wheel\",\n \"2020-01-14\",\n \"64-9249984\",\n true,\n \"132\",\n 5,\n 5\n ],\n [\n false,\n \"Layo\",\n \"China\",\n \"HL Mountain Front Wheel\",\n \"2020-04-24\",\n \"45-0739652\",\n true,\n \"45\",\n 1,\n 5\n ],\n [\n false,\n \"Snaptags\",\n \"United Kingdom\",\n \"LL Touring Handlebars\",\n \"2020-08-06\",\n \"09-5712761\",\n true,\n \"197\",\n 4,\n 2\n ],\n [\n false,\n \"Cogilith\",\n \"China\",\n \"HL Touring Handlebars\",\n \"2020-05-31\",\n \"01-7345008\",\n true,\n \"190\",\n 4,\n 3\n ],\n [\n false,\n \"Reallinks\",\n \"United Kingdom\",\n \"LL Road Front Wheel\",\n \"2020-05-14\",\n \"62-1065350\",\n true,\n \"184\",\n 3,\n 4\n ],\n [\n false,\n \"Quaxo\",\n \"United States\",\n \"ML Road Front Wheel\",\n \"2020-03-23\",\n \"44-7241323\",\n true,\n \"169\",\n 3,\n 4\n ],\n [\n false,\n \"Devify\",\n \"China\",\n \"HL Road Front Wheel\",\n \"2020-12-12\",\n \"52-0295699\",\n false,\n \"152\",\n 4,\n 4\n ],\n [\n false,\n \"Youopia\",\n \"Angola\",\n \"LL Mountain Handlebars\",\n \"2020-04-01\",\n \"52-2650922\",\n false,\n \"182\",\n 6,\n 4\n ],\n [\n false,\n \"Ainyx\",\n \"China\",\n \"Touring Pedal\",\n \"2020-02-27\",\n \"48-3618525\",\n true,\n \"141\",\n 6,\n 1\n ],\n [\n false,\n \"Browsetype\",\n \"Malaysia\",\n \"ML Mountain Handlebars\",\n \"2020-04-28\",\n \"51-8893923\",\n true,\n \"169\",\n 7,\n 1\n ],\n [\n false,\n \"Muxo\",\n \"China\",\n \"HL Mountain Handlebars\",\n \"2020-08-22\",\n \"68-5911361\",\n false,\n \"39\",\n 7,\n 1\n ],\n [\n false,\n \"Bubbletube\",\n \"China\",\n \"LL Road Handlebars\",\n \"2020-10-04\",\n \"41-5880042\",\n true,\n \"71\",\n 8,\n 3\n ],\n [\n false,\n \"Fadeo\",\n \"Vietnam\",\n \"ML Road Handlebars\",\n \"2020-04-23\",\n \"90-5913983\",\n true,\n \"148\",\n 10,\n 3\n ],\n [\n false,\n \"Yadel\",\n \"United Kingdom\",\n \"HL Road Handlebars\",\n \"2020-04-18\",\n \"92-0960699\",\n true,\n \"116\",\n 8,\n 1\n ],\n [\n false,\n \"Blognation\",\n \"China\",\n \"LL Headset\",\n \"2020-01-10\",\n \"06-9493898\",\n true,\n \"96\",\n 10,\n 1\n ],\n [\n false,\n \"Devpoint\",\n \"China\",\n \"ML Headset\",\n \"2020-12-25\",\n \"69-5878565\",\n true,\n \"35\",\n 4,\n 2\n ],\n [\n false,\n \"Aibox\",\n \"United Kingdom\",\n \"HL Headset\",\n \"2020-03-18\",\n \"13-1133017\",\n true,\n \"16\",\n 8,\n 2\n ],\n [\n false,\n \"Brightdog\",\n \"China\",\n \"LL Mountain Pedal\",\n \"2020-09-11\",\n \"39-6530433\",\n true,\n \"194\",\n 2,\n 5\n ],\n [\n false,\n \"Gabcube\",\n \"Nigeria\",\n \"ML Mountain Pedal\",\n \"2020-04-22\",\n \"96-6860388\",\n true,\n \"24\",\n 1,\n 3\n ],\n [\n false,\n \"Muxo\",\n \"China\",\n \"HL Mountain Pedal\",\n \"2020-06-05\",\n \"30-0356137\",\n true,\n \"170\",\n 4,\n 4\n ],\n [\n false,\n \"Tambee\",\n \"China\",\n \"ML Touring Seat/Saddle\",\n \"2020-02-22\",\n \"93-9058255\",\n true,\n \"184\",\n 9,\n 5\n ],\n [\n false,\n \"Cogilith\",\n \"India\",\n \"LL Touring Seat/Saddle\",\n \"2020-04-06\",\n \"82-9268909\",\n false,\n \"153\",\n 10,\n 4\n ],\n [\n false,\n \"Dynabox\",\n \"Hong Kong\",\n \"HL Touring Seat/Saddle\",\n \"2020-01-10\",\n \"20-6913815\",\n false,\n \"88\",\n 10,\n 1\n ],\n [\n false,\n \"Shuffledrive\",\n \"Sudan\",\n \"LL Road Pedal\",\n \"2020-09-16\",\n \"08-8238817\",\n true,\n \"57\",\n 9,\n 2\n ],\n [\n false,\n \"Fivechat\",\n \"China\",\n \"ML Road Pedal\",\n \"2020-08-26\",\n \"44-7370350\",\n false,\n \"62\",\n 4,\n 1\n ],\n [\n false,\n \"Meembee\",\n \"United States\",\n \"HL Road Pedal\",\n \"2020-12-27\",\n \"01-3525949\",\n true,\n \"123\",\n 2,\n 4\n ],\n [\n false,\n \"Dynazzy\",\n \"United Kingdom\",\n \"LL Mountain Seat/Saddle 1\",\n \"2020-12-15\",\n \"04-2414623\",\n true,\n \"77\",\n 10,\n 5\n ],\n [\n false,\n \"Eare\",\n \"China\",\n \"ML Mountain Seat/Saddle 1\",\n \"2020-04-04\",\n \"15-1917509\",\n false,\n \"199\",\n 9,\n 4\n ],\n [\n false,\n \"Yozio\",\n \"China\",\n \"HL Mountain Seat/Saddle 1\",\n \"2020-03-15\",\n \"06-2526845\",\n true,\n \"149\",\n 8,\n 2\n ],\n [\n false,\n \"Quinu\",\n \"Xi'an\",\n \"425-777-7829\",\n \"2020-02-22\",\n \"83-1713558\",\n false,\n \"191\",\n 9,\n 5\n ],\n [\n false,\n \"Jazzy\",\n \"United Kingdom\",\n \"ML Road Seat/Saddle 1\",\n \"2020-08-07\",\n \"00-8892524\",\n true,\n \"150\",\n 10,\n 2\n ],\n [\n false,\n \"Thoughtsphere\",\n \"China\",\n \"HL Road Seat/Saddle 1\",\n \"2020-11-28\",\n \"39-5538991\",\n true,\n \"130\",\n 7,\n 3\n ],\n [\n false,\n \"Leenti\",\n \"China\",\n \"ML Road Rear Wheel\",\n \"2020-12-29\",\n \"06-9002973\",\n true,\n \"179\",\n 1,\n 2\n ],\n [\n false,\n \"Quaxo\",\n \"United Kingdom\",\n \"HL Road Rear Wheel\",\n \"2020-09-06\",\n \"73-6104901\",\n true,\n \"98\",\n 5,\n 3\n ],\n [\n false,\n \"Tanoodle\",\n \"Chile\",\n \"LL Mountain Seat/Saddle 2\",\n \"2020-05-24\",\n \"68-7384479\",\n true,\n \"175\",\n 2,\n 3\n ],\n [\n false,\n \"Feednation\",\n \"China\",\n \"ML Mountain Seat/Saddle 2\",\n \"2020-11-21\",\n \"26-7757763\",\n true,\n \"11\",\n 1,\n 3\n ],\n [\n false,\n \"Kayveo\",\n \"China\",\n \"HL Mountain Seat/Saddle 2\",\n \"2020-06-21\",\n \"07-4873562\",\n false,\n \"184\",\n 7,\n 4\n ],\n [\n false,\n \"Meevee\",\n \"Saudi Arabia\",\n \"LL Road Seat/Saddle 1\",\n \"2020-11-16\",\n \"46-5819554\",\n false,\n \"27\",\n 9,\n 3\n ],\n [\n false,\n \"Twitterworks\",\n \"China\",\n \"ML Road Seat/Saddle 2\",\n \"2020-04-19\",\n \"01-2666826\",\n true,\n \"186\",\n 3,\n 2\n ],\n [\n false,\n \"Wikizz\",\n \"Tanzania\",\n \"HL Road Seat/Saddle 2\",\n \"2020-03-08\",\n \"54-7090503\",\n true,\n \"20\",\n 3,\n 3\n ],\n [\n false,\n \"Yoveo\",\n \"United States\",\n \"LL Mountain Tire\",\n \"2020-10-14\",\n \"78-7658520\",\n false,\n \"153\",\n 2,\n 1\n ],\n [\n false,\n \"Yakidoo\",\n \"China\",\n \"ML Mountain Tire\",\n \"2020-10-12\",\n \"23-9926318\",\n true,\n \"161\",\n 8,\n 5\n ],\n [\n false,\n \"Oyope\",\n \"China\",\n \"HL Mountain Tire\",\n \"2020-09-20\",\n \"20-0179517\",\n true,\n \"98\",\n 10,\n 5\n ],\n [\n false,\n \"Skipstorm\",\n \"United States\",\n \"LL Road Tire\",\n \"2020-10-01\",\n \"02-9543343\",\n true,\n \"30\",\n 7,\n 3\n ],\n [\n false,\n \"Minyx\",\n \"United States\",\n \"ML Road Tire\",\n \"2020-07-07\",\n \"98-3938169\",\n true,\n \"73\",\n 10,\n 2\n ],\n [\n false,\n \"Miboo\",\n \"China\",\n \"HL Road Tire\",\n \"2020-07-25\",\n \"68-5197934\",\n true,\n \"158\",\n 9,\n 1\n ],\n [\n false,\n \"Realfire\",\n \"United States\",\n \"Touring Tire\",\n \"2020-08-27\",\n \"39-8260460\",\n true,\n \"122\",\n 5,\n 2\n ],\n [\n false,\n \"Shufflester\",\n \"China\",\n \"Mountain Tire Tube\",\n \"2020-06-08\",\n \"45-9776170\",\n true,\n \"33\",\n 2,\n 4\n ],\n [\n false,\n \"Ntag\",\n \"China\",\n \"Road Tire Tube\",\n \"2020-12-06\",\n \"45-0858451\",\n true,\n \"107\",\n 6,\n 2\n ],\n [\n false,\n \"Jabberbean\",\n \"United States\",\n \"Touring Tire Tube\",\n \"2020-04-26\",\n \"15-4247305\",\n true,\n \"15\",\n 1,\n 2\n ],\n [\n false,\n \"Thoughtblab\",\n \"China\",\n \"LL Bottom Bracket\",\n \"2020-05-21\",\n \"15-8534931\",\n true,\n \"168\",\n 5,\n 2\n ],\n [\n false,\n \"Jabbertype\",\n \"China\",\n \"Classic Vest\",\n \"2020-07-25\",\n \"23-1251557\",\n true,\n \"135\",\n 4,\n 2\n ],\n [\n false,\n \"Buzzshare\",\n \"United Kingdom\",\n \"Cycling Cap\",\n \"2020-07-07\",\n \"86-5920601\",\n true,\n \"11\",\n 1,\n 4\n ],\n [\n false,\n \"Roodel\",\n \"United States\",\n \"Full-Finger Gloves\",\n \"2020-01-13\",\n \"48-1055459\",\n true,\n \"41\",\n 6,\n 4\n ],\n [\n false,\n \"Zoovu\",\n \"China\",\n \"Half-Finger Gloves\",\n \"2020-06-03\",\n \"12-7842022\",\n true,\n \"144\",\n 6,\n 1\n ],\n [\n false,\n \"Photofeed\",\n \"China\",\n \"HL Mountain Frame\",\n \"2020-07-14\",\n \"94-5088099\",\n true,\n \"106\",\n 1,\n 4\n ]\n];\n\nexport const ODD_ROW_CLASS = \"odd\";\n", + "/src/hooksCallbacks.ts": "import Handsontable from \"handsontable\";\nimport { ODD_ROW_CLASS } from \"./constants\";\n\nconst headerAlignments = new Map([\n [\"9\", \"htCenter\"],\n [\"10\", \"htRight\"],\n [\"12\", \"htCenter\"],\n]);\n\ntype AddClassesToRows = (\n TD: HTMLTableCellElement,\n row: number,\n column: number,\n prop: number | string,\n value: any,\n cellProperties: Handsontable.CellProperties\n) => void;\n\nexport const addClassesToRows: AddClassesToRows = (\n TD,\n row,\n column,\n prop,\n value,\n cellProperties\n) => {\n // Adding classes to `TR` just while rendering first visible `TD` element\n if (column !== 0) {\n return;\n }\n\n const parentElement = TD.parentElement;\n\n if (parentElement === null) {\n return;\n }\n\n // Add class to odd TRs\n if (row % 2 === 0) {\n Handsontable.dom.addClass(parentElement, ODD_ROW_CLASS);\n } else {\n Handsontable.dom.removeClass(parentElement, ODD_ROW_CLASS);\n }\n};\n\nexport function alignHeaders(\n this: Handsontable,\n column: number,\n TH: HTMLTableCellElement\n) {\n if (column < 0) {\n return;\n }\n\n const alignmentClass = this.isRtl() ? \"htRight\" : \"htLeft\";\n\n if (TH.firstChild) {\n if (headerAlignments.has(column.toString())) {\n Handsontable.dom.removeClass(\n TH.firstChild as HTMLElement,\n alignmentClass\n );\n Handsontable.dom.addClass(\n TH.firstChild as HTMLElement,\n headerAlignments.get(column.toString()) as string\n );\n } else {\n Handsontable.dom.addClass(TH.firstChild as HTMLElement, alignmentClass);\n }\n }\n}\n", + "/src/index.tsx": "import React from 'react';\nimport { createRoot } from 'react-dom/client';\nimport './styles.css';\nimport Handsontable from 'handsontable';\nimport { HotTable, HotColumn } from '@handsontable/react-wrapper';\nimport { data } from './constants';\n\nimport { addClassesToRows, alignHeaders } from './hooksCallbacks';\n\nimport 'handsontable/styles/handsontable.min.css';\nimport 'handsontable/styles/ht-theme-main.min.css';\n\nconst App = () => {\n return (\n \n \n \n \n \n \n \n \n \n );\n};\n\nconst rootElement = document.getElementById('root');\n\ncreateRoot(rootElement as HTMLElement).render();\n\nconsole.log(\n `Handsontable: v${Handsontable.version} (${Handsontable.buildDate}) Wrapper: v${HotTable.version} React: v${React.version}`\n);", + "/src/react-app-env.d.ts": "/// \n", + "/src/styles.css": "/*\n A stylesheet customizing app (custom renderers)\n*/\ntable.htCore tr.odd td {\n background: #fafbff;\n}\n\n/*\n A stylesheet customizing Handsontable style\n*/\n.handsontable {\n font-size: 13px;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", \"Roboto\", \"Oxygen\",\n \"Ubuntu\", \"Helvetica Neue\", Arial, sans-serif;\n font-weight: 400;\n}\n\n.collapsibleIndicator {\n text-align: center;\n}\n", + "/tsconfig.app.json": "{\n \"compilerOptions\": {\n \"tsBuildInfoFile\": \"./node_modules/.tmp/tsconfig.app.tsbuildinfo\",\n \"target\": \"es2023\",\n \"lib\": [\"ES2023\", \"DOM\"],\n \"module\": \"esnext\",\n \"types\": [\"vite/client\"],\n \"skipLibCheck\": true,\n\n /* Bundler mode */\n \"moduleResolution\": \"bundler\",\n \"allowImportingTsExtensions\": true,\n \"verbatimModuleSyntax\": true,\n \"moduleDetection\": \"force\",\n \"noEmit\": true,\n \"jsx\": \"react-jsx\",\n\n /* Linting */\n \"noUnusedLocals\": true,\n \"noUnusedParameters\": true,\n \"erasableSyntaxOnly\": true,\n \"noFallthroughCasesInSwitch\": true\n },\n \"include\": [\"src\"]\n}\n", + "/tsconfig.json": "{\n \"files\": [],\n \"references\": [\n { \"path\": \"./tsconfig.app.json\" },\n { \"path\": \"./tsconfig.node.json\" }\n ]\n}\n", + "/tsconfig.node.json": "{\n \"compilerOptions\": {\n \"tsBuildInfoFile\": \"./node_modules/.tmp/tsconfig.node.tsbuildinfo\",\n \"target\": \"es2023\",\n \"lib\": [\"ES2023\"],\n \"module\": \"esnext\",\n \"types\": [\"node\"],\n \"skipLibCheck\": true,\n\n /* Bundler mode */\n \"moduleResolution\": \"bundler\",\n \"allowImportingTsExtensions\": true,\n \"verbatimModuleSyntax\": true,\n \"moduleDetection\": \"force\",\n \"noEmit\": true,\n\n /* Linting */\n \"noUnusedLocals\": true,\n \"noUnusedParameters\": true,\n \"erasableSyntaxOnly\": true,\n \"noFallthroughCasesInSwitch\": true\n },\n \"include\": [\"vite.config.ts\"]\n}\n", + "/vite.config.ts": "import { defineConfig } from 'vite'\nimport react from '@vitejs/plugin-react'\n\n// https://vite.dev/config/\nexport default defineConfig({\n plugins: [react()],\n})\n" + } + }, + { + "framework": "react-js", + "displayName": "React (Vite, JS)", + "tier": 1, + "sandpackTemplate": "react", + "container": null, + "htWrappers": [ + "@handsontable/react-wrapper" + ], + "entry": "/index.jsx", + "htmlEntry": "/index.html", + "devCommand": null, + "buildCommand": "vite build", + "outputDir": "dist", + "outputGlob": null, + "staticExport": false, + "spaMode": false, + "port": null, + "installCommand": "npm install", + "htCoreRange": "18.0.0", + "fileCount": 10, + "assets": [], + "skipped": [], + "files": { + "/LICENSE.txt": "Copyright (c) HANDSONCODE sp. z o. o.\n\nHANDSONTABLE is a software distributed by HANDSONCODE sp. z o. o.,\na Polish corporation, based in Gdynia, Poland, at 96/98 Aleja Zwycięstwa,\nregistered with the National Court Register under number 538651,\nEU tax ID number: PL5862294002, share capital: PLN 62,800.00.\n\nThis software is protected by applicable copyright laws, including\ninternational treaties, and dual-licensed – depending on whether\nyour use is intended for or may result in commercial advantage\nor monetary compensation (commercial purposes), or not.\n\nIf your use involves only such purposes as research, private study,\nevaluation and the like, you agree to be bound by the terms included\nin the \"handsontable-non-commercial-license.pdf\" file, available\nin the main directory of this software repository.\n\nBy installing, copying, or otherwise using this software for\ncommercial purposes, you agree to be bound by the terms included\nin the \"handsontable-general-terms.pdf\" file, available in the main\ndirectory of this software repository.\n\nHANDSONCODE PROVIDES THIS SOFTWARE ON AN \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND. IN NO EVENT\nAND UNDER NO LEGAL THEORY, SHALL HANDSONCODE BE LIABLE\nTO YOU FOR DAMAGES, INCLUDING ANY DIRECT, INDIRECT, SPECIAL,\nINCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER ARISING\nFROM USE OR INABILITY TO USE THIS SOFTWARE.\n", + "/README.md": "# Demo\n\n## Description\n\nA general purpose demo showcasing some of the most popular features of Handsontable in React.\n\n## Installation\n\nInstall all necessary dependencies from npm with `npm install`.\n\n## Development\n\nTo start local development server use `npm run start`. You can then visit http://localhost:8080 to view the project.\n\n## Testing\n\nTo run tests for this project, first launch the development server with `npm run start`. Once the server is running, execute `npm run test` to run the test specs.\n\n## Forking\n\nThere are two ways to fork this project in Handsontable's monorepo:\n\n1. Fork the entire repository.\n2. Copy the example to a new repository. Detailed instructions are available [here](../../../../README.md#copying-an-example-to-a-separate-repo) in the section _Copying an example to a separate repo_.\n\n## License\n\nHandsontable is a commercial software with two licenses available:\n\n- Free for non-commercial purposes such as teaching, academic research, and evaluation. [Read it here](https://github.com/handsontable/handsontable/blob/master/handsontable-non-commercial-license.pdf).\n- Commercial license with support and maintenance included. See [pricing plans](https://handsontable.com/pricing).\n\n## Get help\n\nJoin our [community](https://github.com/handsontable/handsontable/discussions) to get help, share ideas, and contribute to the ongoing development of Handsontable!\n\nCommercial license holders with an active support plan can contact our [Support Team](https://handsontable.com/contact?category=technical_support).\n", + "/config-overrides.js": "module.exports = (config, ...rest) => {\n return { ...config, resolve: { ...config.resolve, symlinks: false } };\n};\n", + "/index.html": "\n\n\n\n Handsontable for React JS example\n \n\t\n\n\n\n
\n \n\n\n", + "/index.jsx": "import React from 'react';\nimport { createRoot } from 'react-dom/client';\nimport './src/styles.css';\nimport Handsontable from 'handsontable';\nimport { HotTable, HotColumn } from '@handsontable/react-wrapper';\nimport { data } from './src/constants';\n\nimport { addClassesToRows, alignHeaders } from './src/hooksCallbacks';\n\nimport 'handsontable/styles/handsontable.min.css';\nimport 'handsontable/styles/ht-theme-main.min.css';\n\nconst App = () => {\n return (\n \n \n \n \n \n \n \n \n \n );\n};\n\nconst rootElement = document.getElementById('root');\n\ncreateRoot(rootElement).render();\n\nconsole.log(\n `Handsontable: v${Handsontable.version} (${Handsontable.buildDate}) Wrapper: v${HotTable.version} React: v${React.version}`\n);", + "/package.json": "{\n \"name\": \"react-demo\",\n \"version\": \"0.0.0\",\n \"private\": true,\n \"homepage\": \"https://handsontable.com/\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/handsontable/handsontable.git\"\n },\n \"bugs\": {\n \"url\": \"https://github.com/handsontable/handsontable/issues\"\n },\n \"author\": \"Handsoncode \",\n \"dependencies\": {\n \"react\": \"19.2.7\",\n \"react-dom\": \"19.2.7\",\n \"@handsontable/react-wrapper\": \"18.0.0\",\n \"handsontable\": \"18.0.0\"\n },\n \"scripts\": {\n \"dev\": \"vite --port 8080\",\n \"build\": \"vite build\",\n \"preview\": \"vite preview\"\n },\n \"devDependencies\": {\n \"@vitejs/plugin-react\": \"^6.0.3\",\n \"vite\": \"^8.1.1\"\n },\n \"license\": \"SEE LICENSE IN LICENSE.txt\",\n \"browserslist\": {\n \"production\": [\n \">0.2%\",\n \"not dead\",\n \"not op_mini all\"\n ],\n \"development\": [\n \"last 1 chrome version\",\n \"last 1 firefox version\",\n \"last 1 safari version\"\n ]\n },\n \"config\": {\n \"codesandbox\": {\n \"templateId\": \"vmjhgx\"\n },\n \"stackblitz\": {\n \"template\": \"create-react-app\"\n }\n }\n}\n", + "/src/constants.js": "export const data = [\n [\n false,\n \"Tagcat\",\n \"United Kingdom\",\n \"Classic Vest\",\n \"2020-10-11\",\n \"01-2331942\",\n true,\n \"172\",\n 2,\n 2\n ],\n [\n true,\n \"Zoomzone\",\n \"Indonesia\",\n \"Cycling Cap\",\n \"2020-05-03\",\n \"88-2768633\",\n true,\n \"188\",\n 6,\n 2\n ],\n [\n true,\n \"Meeveo\",\n \"United States\",\n \"Full-Finger Gloves\",\n \"2020-03-27\",\n \"51-6775945\",\n true,\n \"162\",\n 1,\n 3\n ],\n [\n false,\n \"Buzzdog\",\n \"Philippines\",\n \"HL Mountain Frame\",\n \"2020-08-29\",\n \"44-4028109\",\n true,\n \"133\",\n 7,\n 1\n ],\n [\n true,\n \"Katz\",\n \"India\",\n \"Half-Finger Gloves\",\n \"2020-10-02\",\n \"08-2758492\",\n true,\n \"87\",\n 1,\n 3\n ],\n [\n false,\n \"Jaxbean\",\n \"China\",\n \"HL Road Frame\",\n \"2020-09-28\",\n \"84-3557705\",\n false,\n \"26\",\n 8,\n 1\n ],\n [\n false,\n \"Wikido\",\n \"Brazil\",\n \"HL Touring Frame\",\n \"2020-06-24\",\n \"20-9397637\",\n false,\n \"110\",\n 4,\n 1\n ],\n [\n false,\n \"Browsedrive\",\n \"United States\",\n \"LL Mountain Frame\",\n \"2020-03-13\",\n \"36-0079556\",\n true,\n \"50\",\n 4,\n 4\n ],\n [\n false,\n \"Twinder\",\n \"United Kingdom\",\n \"LL Road Frame\",\n \"2020-04-06\",\n \"41-1489542\",\n false,\n \"160\",\n 6,\n 1\n ],\n [\n false,\n \"Jetwire\",\n \"China\",\n \"LL Touring Frame\",\n \"2020-02-01\",\n \"37-1531629\",\n true,\n \"30\",\n 8,\n 5\n ],\n [\n false,\n \"Chatterpoint\",\n \"China\",\n \"Long-Sleeve Logo Jersey\",\n \"2020-07-14\",\n \"25-5083429\",\n true,\n \"39\",\n 7,\n 2\n ],\n [\n false,\n \"Twinder\",\n \"Egypt\",\n \"Men's Bib-Shorts\",\n \"2020-08-31\",\n \"04-4281278\",\n false,\n \"96\",\n 6,\n 1\n ],\n [\n false,\n \"Midel\",\n \"United States\",\n \"Men's Sports Shorts\",\n \"2020-06-27\",\n \"55-1711908\",\n true,\n \"108\",\n 10,\n 3\n ],\n [\n false,\n \"Yodo\",\n \"India\",\n \"ML Mountain Frame\",\n \"2020-03-16\",\n \"58-8360815\",\n false,\n \"46\",\n 1,\n 1\n ],\n [\n false,\n \"Camido\",\n \"Russia\",\n \"ML Mountain Frame-W\",\n \"2020-09-13\",\n \"10-3786104\",\n true,\n \"97\",\n 8,\n 3\n ],\n [\n false,\n \"Eire\",\n \"Thailand\",\n \"ML Road Frame\",\n \"2020-04-10\",\n \"45-1186054\",\n true,\n \"161\",\n 1,\n 4\n ],\n [\n false,\n \"Vinte\",\n \"United Kingdom\",\n \"ML Road Frame-W\",\n \"2020-01-22\",\n \"62-6202742\",\n true,\n \"58\",\n 4,\n 3\n ],\n [\n false,\n \"Twitterlist\",\n \"China\",\n \"Mountain Bike Socks\",\n \"2020-11-09\",\n \"88-9646223\",\n true,\n \"92\",\n 8,\n 3\n ],\n [\n false,\n \"Eidel\",\n \"Bangladesh\",\n \"Mountain-100\",\n \"2020-09-19\",\n \"45-5588112\",\n true,\n \"5\",\n 6,\n 5\n ],\n [\n false,\n \"Trunyx\",\n \"Nigeria\",\n \"Mountain-200\",\n \"2020-03-09\",\n \"66-6271819\",\n true,\n \"158\",\n 4,\n 1\n ],\n [\n false,\n \"Katz\",\n \"Turkey\",\n \"Mountain-300\",\n \"2020-03-05\",\n \"38-9245023\",\n false,\n \"121\",\n 5,\n 4\n ],\n [\n false,\n \"Kaymbo\",\n \"United States\",\n \"Mountain-400-W\",\n \"2020-12-24\",\n \"44-5916927\",\n false,\n \"61\",\n 5,\n 4\n ],\n [\n false,\n \"Ozu\",\n \"Pakistan\",\n \"Mountain-500\",\n \"2020-06-13\",\n \"31-5449914\",\n true,\n \"155\",\n 2,\n 2\n ],\n [\n false,\n \"Rhynyx\",\n \"India\",\n \"Racing Socks\",\n \"2020-12-05\",\n \"19-9413869\",\n true,\n \"162\",\n 2,\n 4\n ],\n [\n false,\n \"Flashset\",\n \"Iran\",\n \"Road-150\",\n \"2020-12-14\",\n \"25-9807605\",\n false,\n \"46\",\n 7,\n 1\n ],\n [\n false,\n \"Yata\",\n \"Congo (Kinshasa)\",\n \"Road-250\",\n \"2020-06-12\",\n \"74-4291983\",\n true,\n \"47\",\n 4,\n 4\n ],\n [\n false,\n \"Brainlounge\",\n \"Vietnam\",\n \"Road-350-W\",\n \"2020-03-10\",\n \"83-0980643\",\n true,\n \"104\",\n 2,\n 3\n ],\n [\n false,\n \"Babblestorm\",\n \"United States\",\n \"Road-450\",\n \"2020-10-10\",\n \"19-2878430\",\n true,\n \"101\",\n 6,\n 4\n ],\n [\n false,\n \"Youspan\",\n \"Brazil\",\n \"Road-550-W\",\n \"2020-12-16\",\n \"19-1838230\",\n true,\n \"150\",\n 10,\n 3\n ],\n [\n false,\n \"Nlounge\",\n \"China\",\n \"Road-650\",\n \"2020-10-31\",\n \"32-2267938\",\n true,\n \"42\",\n 4,\n 2\n ],\n [\n false,\n \"Twinte\",\n \"India\",\n \"Road-750\",\n \"2020-08-17\",\n \"79-2821972\",\n true,\n \"144\",\n 9,\n 3\n ],\n [\n false,\n \"Oyonder\",\n \"United Kingdom\",\n \"Short-Sleeve Classic Jersey\",\n \"2020-12-04\",\n \"46-6597557\",\n true,\n \"195\",\n 4,\n 1\n ],\n [\n false,\n \"Gigabox\",\n \"Pakistan\",\n \"Sport-100\",\n \"2020-02-03\",\n \"15-1793960\",\n true,\n \"199\",\n 4,\n 4\n ],\n [\n false,\n \"Livetube\",\n \"France\",\n \"Touring-1000\",\n \"2020-05-16\",\n \"86-0811003\",\n true,\n \"110\",\n 4,\n 5\n ],\n [\n false,\n \"Voomm\",\n \"United Kingdom\",\n \"Touring-2000\",\n \"2020-07-15\",\n \"95-3068680\",\n true,\n \"51\",\n 4,\n 4\n ],\n [\n false,\n \"Voonyx\",\n \"China\",\n \"Touring-3000\",\n \"2020-11-27\",\n \"35-3085360\",\n false,\n \"69\",\n 2,\n 5\n ],\n [\n false,\n \"Zoombeat\",\n \"United States\",\n \"Women's Mountain Shorts\",\n \"2020-11-03\",\n \"56-8673088\",\n false,\n \"53\",\n 2,\n 3\n ],\n [\n false,\n \"Roomm\",\n \"China\",\n \"Women's Tights\",\n \"2020-03-16\",\n \"76-0085918\",\n true,\n \"168\",\n 1,\n 1\n ],\n [\n false,\n \"Leenti\",\n \"China\",\n \"Mountain-400\",\n \"2020-05-16\",\n \"03-0893276\",\n false,\n \"58\",\n 1,\n 4\n ],\n [\n false,\n \"Jetpulse\",\n \"United States\",\n \"Road-550\",\n \"2020-02-08\",\n \"79-9013306\",\n true,\n \"152\",\n 9,\n 3\n ],\n [\n false,\n \"Katz\",\n \"Peru\",\n \"Road-350\",\n \"2020-02-15\",\n \"55-7799920\",\n true,\n \"66\",\n 4,\n 2\n ],\n [\n false,\n \"Cogidoo\",\n \"India\",\n \"LL Mountain Front Wheel\",\n \"2020-06-04\",\n \"07-0881122\",\n false,\n \"112\",\n 9,\n 2\n ],\n [\n false,\n \"Divavu\",\n \"Colombia\",\n \"Touring Rear Wheel\",\n \"2020-02-24\",\n \"58-6157387\",\n true,\n \"50\",\n 10,\n 4\n ],\n [\n false,\n \"Mydeo\",\n \"China\",\n \"Touring Front Wheel\",\n \"2020-12-07\",\n \"12-2810010\",\n false,\n \"31\",\n 3,\n 5\n ],\n [\n false,\n \"Browsebug\",\n \"Japan\",\n \"ML Mountain Front Wheel\",\n \"2020-01-14\",\n \"64-9249984\",\n true,\n \"132\",\n 5,\n 5\n ],\n [\n false,\n \"Layo\",\n \"China\",\n \"HL Mountain Front Wheel\",\n \"2020-04-24\",\n \"45-0739652\",\n true,\n \"45\",\n 1,\n 5\n ],\n [\n false,\n \"Snaptags\",\n \"United Kingdom\",\n \"LL Touring Handlebars\",\n \"2020-08-06\",\n \"09-5712761\",\n true,\n \"197\",\n 4,\n 2\n ],\n [\n false,\n \"Cogilith\",\n \"China\",\n \"HL Touring Handlebars\",\n \"2020-05-31\",\n \"01-7345008\",\n true,\n \"190\",\n 4,\n 3\n ],\n [\n false,\n \"Reallinks\",\n \"United Kingdom\",\n \"LL Road Front Wheel\",\n \"2020-05-14\",\n \"62-1065350\",\n true,\n \"184\",\n 3,\n 4\n ],\n [\n false,\n \"Quaxo\",\n \"United States\",\n \"ML Road Front Wheel\",\n \"2020-03-23\",\n \"44-7241323\",\n true,\n \"169\",\n 3,\n 4\n ],\n [\n false,\n \"Devify\",\n \"China\",\n \"HL Road Front Wheel\",\n \"2020-12-12\",\n \"52-0295699\",\n false,\n \"152\",\n 4,\n 4\n ],\n [\n false,\n \"Youopia\",\n \"Angola\",\n \"LL Mountain Handlebars\",\n \"2020-04-01\",\n \"52-2650922\",\n false,\n \"182\",\n 6,\n 4\n ],\n [\n false,\n \"Ainyx\",\n \"China\",\n \"Touring Pedal\",\n \"2020-02-27\",\n \"48-3618525\",\n true,\n \"141\",\n 6,\n 1\n ],\n [\n false,\n \"Browsetype\",\n \"Malaysia\",\n \"ML Mountain Handlebars\",\n \"2020-04-28\",\n \"51-8893923\",\n true,\n \"169\",\n 7,\n 1\n ],\n [\n false,\n \"Muxo\",\n \"China\",\n \"HL Mountain Handlebars\",\n \"2020-08-22\",\n \"68-5911361\",\n false,\n \"39\",\n 7,\n 1\n ],\n [\n false,\n \"Bubbletube\",\n \"China\",\n \"LL Road Handlebars\",\n \"2020-10-04\",\n \"41-5880042\",\n true,\n \"71\",\n 8,\n 3\n ],\n [\n false,\n \"Fadeo\",\n \"Vietnam\",\n \"ML Road Handlebars\",\n \"2020-04-23\",\n \"90-5913983\",\n true,\n \"148\",\n 10,\n 3\n ],\n [\n false,\n \"Yadel\",\n \"United Kingdom\",\n \"HL Road Handlebars\",\n \"2020-04-18\",\n \"92-0960699\",\n true,\n \"116\",\n 8,\n 1\n ],\n [\n false,\n \"Blognation\",\n \"China\",\n \"LL Headset\",\n \"2020-01-10\",\n \"06-9493898\",\n true,\n \"96\",\n 10,\n 1\n ],\n [\n false,\n \"Devpoint\",\n \"China\",\n \"ML Headset\",\n \"2020-12-25\",\n \"69-5878565\",\n true,\n \"35\",\n 4,\n 2\n ],\n [\n false,\n \"Aibox\",\n \"United Kingdom\",\n \"HL Headset\",\n \"2020-03-18\",\n \"13-1133017\",\n true,\n \"16\",\n 8,\n 2\n ],\n [\n false,\n \"Brightdog\",\n \"China\",\n \"LL Mountain Pedal\",\n \"2020-09-11\",\n \"39-6530433\",\n true,\n \"194\",\n 2,\n 5\n ],\n [\n false,\n \"Gabcube\",\n \"Nigeria\",\n \"ML Mountain Pedal\",\n \"2020-04-22\",\n \"96-6860388\",\n true,\n \"24\",\n 1,\n 3\n ],\n [\n false,\n \"Muxo\",\n \"China\",\n \"HL Mountain Pedal\",\n \"2020-06-05\",\n \"30-0356137\",\n true,\n \"170\",\n 4,\n 4\n ],\n [\n false,\n \"Tambee\",\n \"China\",\n \"ML Touring Seat/Saddle\",\n \"2020-02-22\",\n \"93-9058255\",\n true,\n \"184\",\n 9,\n 5\n ],\n [\n false,\n \"Cogilith\",\n \"India\",\n \"LL Touring Seat/Saddle\",\n \"2020-04-06\",\n \"82-9268909\",\n false,\n \"153\",\n 10,\n 4\n ],\n [\n false,\n \"Dynabox\",\n \"Hong Kong\",\n \"HL Touring Seat/Saddle\",\n \"2020-01-10\",\n \"20-6913815\",\n false,\n \"88\",\n 10,\n 1\n ],\n [\n false,\n \"Shuffledrive\",\n \"Sudan\",\n \"LL Road Pedal\",\n \"2020-09-16\",\n \"08-8238817\",\n true,\n \"57\",\n 9,\n 2\n ],\n [\n false,\n \"Fivechat\",\n \"China\",\n \"ML Road Pedal\",\n \"2020-08-26\",\n \"44-7370350\",\n false,\n \"62\",\n 4,\n 1\n ],\n [\n false,\n \"Meembee\",\n \"United States\",\n \"HL Road Pedal\",\n \"2020-12-27\",\n \"01-3525949\",\n true,\n \"123\",\n 2,\n 4\n ],\n [\n false,\n \"Dynazzy\",\n \"United Kingdom\",\n \"LL Mountain Seat/Saddle 1\",\n \"2020-12-15\",\n \"04-2414623\",\n true,\n \"77\",\n 10,\n 5\n ],\n [\n false,\n \"Eare\",\n \"China\",\n \"ML Mountain Seat/Saddle 1\",\n \"2020-04-04\",\n \"15-1917509\",\n false,\n \"199\",\n 9,\n 4\n ],\n [\n false,\n \"Yozio\",\n \"China\",\n \"HL Mountain Seat/Saddle 1\",\n \"2020-03-15\",\n \"06-2526845\",\n true,\n \"149\",\n 8,\n 2\n ],\n [\n false,\n \"Quinu\",\n \"Xi'an\",\n \"425-777-7829\",\n \"2020-02-22\",\n \"83-1713558\",\n false,\n \"191\",\n 9,\n 5\n ],\n [\n false,\n \"Jazzy\",\n \"United Kingdom\",\n \"ML Road Seat/Saddle 1\",\n \"2020-08-07\",\n \"00-8892524\",\n true,\n \"150\",\n 10,\n 2\n ],\n [\n false,\n \"Thoughtsphere\",\n \"China\",\n \"HL Road Seat/Saddle 1\",\n \"2020-11-28\",\n \"39-5538991\",\n true,\n \"130\",\n 7,\n 3\n ],\n [\n false,\n \"Leenti\",\n \"China\",\n \"ML Road Rear Wheel\",\n \"2020-12-29\",\n \"06-9002973\",\n true,\n \"179\",\n 1,\n 2\n ],\n [\n false,\n \"Quaxo\",\n \"United Kingdom\",\n \"HL Road Rear Wheel\",\n \"2020-09-06\",\n \"73-6104901\",\n true,\n \"98\",\n 5,\n 3\n ],\n [\n false,\n \"Tanoodle\",\n \"Chile\",\n \"LL Mountain Seat/Saddle 2\",\n \"2020-05-24\",\n \"68-7384479\",\n true,\n \"175\",\n 2,\n 3\n ],\n [\n false,\n \"Feednation\",\n \"China\",\n \"ML Mountain Seat/Saddle 2\",\n \"2020-11-21\",\n \"26-7757763\",\n true,\n \"11\",\n 1,\n 3\n ],\n [\n false,\n \"Kayveo\",\n \"China\",\n \"HL Mountain Seat/Saddle 2\",\n \"2020-06-21\",\n \"07-4873562\",\n false,\n \"184\",\n 7,\n 4\n ],\n [\n false,\n \"Meevee\",\n \"Saudi Arabia\",\n \"LL Road Seat/Saddle 1\",\n \"2020-11-16\",\n \"46-5819554\",\n false,\n \"27\",\n 9,\n 3\n ],\n [\n false,\n \"Twitterworks\",\n \"China\",\n \"ML Road Seat/Saddle 2\",\n \"2020-04-19\",\n \"01-2666826\",\n true,\n \"186\",\n 3,\n 2\n ],\n [\n false,\n \"Wikizz\",\n \"Tanzania\",\n \"HL Road Seat/Saddle 2\",\n \"2020-03-08\",\n \"54-7090503\",\n true,\n \"20\",\n 3,\n 3\n ],\n [\n false,\n \"Yoveo\",\n \"United States\",\n \"LL Mountain Tire\",\n \"2020-10-14\",\n \"78-7658520\",\n false,\n \"153\",\n 2,\n 1\n ],\n [\n false,\n \"Yakidoo\",\n \"China\",\n \"ML Mountain Tire\",\n \"2020-10-12\",\n \"23-9926318\",\n true,\n \"161\",\n 8,\n 5\n ],\n [\n false,\n \"Oyope\",\n \"China\",\n \"HL Mountain Tire\",\n \"2020-09-20\",\n \"20-0179517\",\n true,\n \"98\",\n 10,\n 5\n ],\n [\n false,\n \"Skipstorm\",\n \"United States\",\n \"LL Road Tire\",\n \"2020-10-01\",\n \"02-9543343\",\n true,\n \"30\",\n 7,\n 3\n ],\n [\n false,\n \"Minyx\",\n \"United States\",\n \"ML Road Tire\",\n \"2020-07-07\",\n \"98-3938169\",\n true,\n \"73\",\n 10,\n 2\n ],\n [\n false,\n \"Miboo\",\n \"China\",\n \"HL Road Tire\",\n \"2020-07-25\",\n \"68-5197934\",\n true,\n \"158\",\n 9,\n 1\n ],\n [\n false,\n \"Realfire\",\n \"United States\",\n \"Touring Tire\",\n \"2020-08-27\",\n \"39-8260460\",\n true,\n \"122\",\n 5,\n 2\n ],\n [\n false,\n \"Shufflester\",\n \"China\",\n \"Mountain Tire Tube\",\n \"2020-06-08\",\n \"45-9776170\",\n true,\n \"33\",\n 2,\n 4\n ],\n [\n false,\n \"Ntag\",\n \"China\",\n \"Road Tire Tube\",\n \"2020-12-06\",\n \"45-0858451\",\n true,\n \"107\",\n 6,\n 2\n ],\n [\n false,\n \"Jabberbean\",\n \"United States\",\n \"Touring Tire Tube\",\n \"2020-04-26\",\n \"15-4247305\",\n true,\n \"15\",\n 1,\n 2\n ],\n [\n false,\n \"Thoughtblab\",\n \"China\",\n \"LL Bottom Bracket\",\n \"2020-05-21\",\n \"15-8534931\",\n true,\n \"168\",\n 5,\n 2\n ],\n [\n false,\n \"Jabbertype\",\n \"China\",\n \"Classic Vest\",\n \"2020-07-25\",\n \"23-1251557\",\n true,\n \"135\",\n 4,\n 2\n ],\n [\n false,\n \"Buzzshare\",\n \"United Kingdom\",\n \"Cycling Cap\",\n \"2020-07-07\",\n \"86-5920601\",\n true,\n \"11\",\n 1,\n 4\n ],\n [\n false,\n \"Roodel\",\n \"United States\",\n \"Full-Finger Gloves\",\n \"2020-01-13\",\n \"48-1055459\",\n true,\n \"41\",\n 6,\n 4\n ],\n [\n false,\n \"Zoovu\",\n \"China\",\n \"Half-Finger Gloves\",\n \"2020-06-03\",\n \"12-7842022\",\n true,\n \"144\",\n 6,\n 1\n ],\n [\n false,\n \"Photofeed\",\n \"China\",\n \"HL Mountain Frame\",\n \"2020-07-14\",\n \"94-5088099\",\n true,\n \"106\",\n 1,\n 4\n ]\n];\n\nexport const ODD_ROW_CLASS = \"odd\";\n", + "/src/hooksCallbacks.js": "import Handsontable from \"handsontable\";\nimport { ODD_ROW_CLASS } from \"./constants\";\n\nconst headerAlignments = new Map([\n [\"9\", \"htCenter\"],\n [\"10\", \"htRight\"],\n [\"12\", \"htCenter\"],\n]);\n\n\n\nexport const addClassesToRows = (\n TD,\n row,\n column,\n prop,\n value,\n cellProperties\n) => {\n // Adding classes to `TR` just while rendering first visible `TD` element\n if (column !== 0) {\n return;\n }\n\n const parentElement = TD.parentElement;\n\n if (parentElement === null) {\n return;\n }\n\n // Add class to odd TRs\n if (row % 2 === 0) {\n Handsontable.dom.addClass(parentElement, ODD_ROW_CLASS);\n } else {\n Handsontable.dom.removeClass(parentElement, ODD_ROW_CLASS);\n }\n};\n\nexport function alignHeaders( \n column,\n TH\n) {\n if (column < 0) {\n return;\n }\n\n const alignmentClass = \"htLeft\";\n\n if (TH.firstChild) {\n if (headerAlignments.has(column.toString())) {\n Handsontable.dom.removeClass(\n TH.firstChild,\n alignmentClass\n );\n Handsontable.dom.addClass(\n TH.firstChild,\n headerAlignments.get(column.toString())\n );\n } else {\n Handsontable.dom.addClass(TH.firstChild, alignmentClass);\n }\n }\n}\n", + "/src/styles.css": "/*\n A stylesheet customizing app (custom renderers)\n*/\ntable.htCore tr.odd td {\n background: #fafbff;\n}\n\n/*\n A stylesheet customizing Handsontable style\n*/\n.handsontable {\n font-size: 13px;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", \"Roboto\", \"Oxygen\",\n \"Ubuntu\", \"Helvetica Neue\", Arial, sans-serif;\n font-weight: 400;\n}\n\n.collapsibleIndicator {\n text-align: center;\n}\n", + "/vite.config.js": "import { defineConfig } from 'vite';\nimport react from '@vitejs/plugin-react';\n\nexport default defineConfig({\n plugins: [react()],\n base: './',\n server: {\n port: 8080,\n allowedHosts: true,\n },\n});\n" + } + }, + { + "framework": "ant-design", + "displayName": "Ant Design + React", + "tier": 1, + "sandpackTemplate": "react", + "container": null, + "htWrappers": [ + "@handsontable/react-wrapper" + ], + "entry": "/src/index.jsx", + "htmlEntry": "/index.html", + "devCommand": null, + "buildCommand": "vite build", + "outputDir": "dist", + "outputGlob": null, + "staticExport": false, + "spaMode": false, + "port": null, + "installCommand": "npm install", + "htCoreRange": "17.0.1", + "fileCount": 6, + "assets": [], + "skipped": [], + "files": { + "/LICENSE.txt": "Copyright (c) HANDSONCODE sp. z o. o.\n\nHANDSONTABLE is a software distributed by HANDSONCODE sp. z o. o.,\na Polish corporation, based in Gdynia, Poland, at 96/98 Aleja Zwycięstwa,\nregistered with the National Court Register under number 538651,\nEU tax ID number: PL5862294002, share capital: PLN 62,800.00.\n\nThis software is protected by applicable copyright laws, including\ninternational treaties, and dual-licensed – depending on whether\nyour use is intended for or may result in commercial advantage\nor monetary compensation (commercial purposes), or not.\n\nIf your use involves only such purposes as research, private study,\nevaluation and the like, you agree to be bound by the terms included\nin the \"handsontable-non-commercial-license.pdf\" file, available\nin the main directory of this software repository.\n\nBy installing, copying, or otherwise using this software for\ncommercial purposes, you agree to be bound by the terms included\nin the \"handsontable-general-terms.pdf\" file, available in the main\ndirectory of this software repository.\n\nHANDSONCODE PROVIDES THIS SOFTWARE ON AN \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND. IN NO EVENT\nAND UNDER NO LEGAL THEORY, SHALL HANDSONCODE BE LIABLE\nTO YOU FOR DAMAGES, INCLUDING ANY DIRECT, INDIRECT, SPECIAL,\nINCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER ARISING\nFROM USE OR INABILITY TO USE THIS SOFTWARE.\n", + "/index.html": "\n\n \n \n \n Handsontable with Ant Design\n \n \n
\n \n \n\n", + "/package.json": "{\n \"name\": \"handsontable-ant-design-recipe\",\n \"version\": \"1.0.0\",\n \"private\": true,\n \"description\": \"Handsontable with Ant Design recipe\",\n \"homepage\": \"https://handsontable.com/\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/handsontable/handsontable.git\"\n },\n \"bugs\": {\n \"url\": \"https://github.com/handsontable/handsontable/issues\"\n },\n \"author\": \"Handsoncode \",\n \"license\": \"SEE LICENSE IN LICENSE.txt\",\n \"type\": \"module\",\n \"scripts\": {\n \"start\": \"vite\",\n \"dev\": \"vite\",\n \"build\": \"vite build\",\n \"preview\": \"vite preview\"\n },\n \"dependencies\": {\n \"@handsontable/react-wrapper\": \"17.0.1\",\n \"antd\": \"^5.27.6\",\n \"handsontable\": \"17.0.1\",\n \"react\": \"^18.3.1\",\n \"react-dom\": \"^18.3.1\"\n },\n \"devDependencies\": {\n \"@vitejs/plugin-react\": \"^4.3.4\",\n \"vite\": \"^5.4.11\"\n },\n \"browserslist\": {\n \"production\": [\n \">0.2%\",\n \"not dead\",\n \"not op_mini all\"\n ],\n \"development\": [\n \"last 1 chrome version\",\n \"last 1 firefox version\",\n \"last 1 safari version\"\n ]\n },\n \"config\": {\n \"codesandbox\": {\n \"templateId\": \"8y5qy5\"\n }\n }\n}\n", + "/src/App.jsx": "import React, { useCallback } from 'react';\nimport { Card, Space, Tag, Typography } from 'antd';\nimport { HotTable, HotColumn } from '@handsontable/react-wrapper';\nimport { registerAllModules } from 'handsontable/registry';\nimport { getTheme, hasTheme, horizonTheme, registerTheme } from 'handsontable/themes';\nimport colorsAnt from 'handsontable/themes/static/variables/colors/ant';\n\nregisterAllModules();\n\nconst data = [\n {\n key: '1',\n name: 'John Brown',\n age: 32,\n address: 'New York No. 1 Lake Park',\n tags: ['nice', 'developer'],\n actionHint: null,\n },\n {\n key: '2',\n name: 'Jim Green',\n age: 42,\n address: 'London No. 1 Lake Park',\n tags: ['loser'],\n actionHint: null,\n },\n {\n key: '3',\n name: 'Joe Black',\n age: 32,\n address: 'Sydney No. 1 Lake Park',\n tags: ['cool', 'teacher'],\n actionHint: null,\n },\n];\n\nconst THEME_NAME = 'horizon-ant-table';\n\nconst antTableTheme = (() => {\n if (hasTheme(THEME_NAME)) {\n return getTheme(THEME_NAME);\n }\n return registerTheme(THEME_NAME, horizonTheme)\n .params({\n colors: colorsAnt,\n tokens: {\n wrapperBorderColor: ['colors.palette.200', 'colors.palette.700'],\n wrapperBorderRadius: '8px',\n headerBackgroundColor: ['colors.palette.100', 'colors.palette.800'],\n headerFontWeight: '600',\n cellHorizontalBorderColor: ['colors.palette.200', 'colors.palette.700'],\n cellVerticalBorderColor: ['colors.palette.200', 'colors.palette.700'],\n cellHorizontalPadding: '16px',\n cellVerticalPadding: '8px',\n rowCellEvenBackgroundColor: ['colors.white', 'colors.palette.950'],\n rowCellOddBackgroundColor: ['colors.white', 'colors.palette.950'],\n cellReadOnlyBackgroundColor: ['colors.white', 'colors.palette.950'],\n foregroundColor: ['colors.palette.800', 'colors.palette.100'],\n linkColor: ['colors.primary.200', 'colors.primary.100'],\n linkHoverColor: ['colors.primary.100', 'colors.primary.200'],\n },\n })\n .setColorScheme('light')\n .setDensityType('comfortable');\n})();\n\nfunction NameCell({ value }) {\n const label = value != null ? String(value) : '';\n return (\n e.preventDefault()}>\n {label}\n \n );\n}\n\nfunction TagsCell({ value }) {\n const tags = Array.isArray(value) ? value : [];\n if (tags.length === 0) {\n return null;\n }\n return (\n \n {tags.map((tag) => {\n let color = tag.length > 5 ? 'geekblue' : 'green';\n if (tag === 'loser') {\n color = 'volcano';\n }\n return (\n \n {String(tag).toUpperCase()}\n \n );\n })}\n \n );\n}\n\nfunction ActionCell({ instance, row }) {\n const rowData = instance.getSourceDataAtRow(row);\n return (\n \n e.preventDefault()}>\n Invite {rowData?.name ?? ''}\n \n e.preventDefault()}>\n Delete\n \n \n );\n}\n\nfunction AntLikeGrid() {\n const readOnlyCell = useCallback(() => ({ readOnly: true }), []);\n\n return (\n \n \n \n \n \n \n \n );\n}\n\nexport default function App() {\n return (\n
\n Handsontable with Ant Design theme tokens}>\n \n \n
\n );\n}\n", + "/src/index.jsx": "import React from 'react';\nimport { createRoot } from 'react-dom/client';\nimport App from './App';\n\nconst rootElement = document.getElementById('root');\nconst root = createRoot(rootElement);\nroot.render();\n", + "/vite.config.js": "import { defineConfig } from 'vite';\nimport react from '@vitejs/plugin-react';\n\nexport default defineConfig({\n plugins: [react()],\n server: {\n allowedHosts: true, // CodeSandbox preview hosts (*.csb.app)\n },\n optimizeDeps: {\n include: ['handsontable', '@handsontable/react-wrapper', 'antd'],\n },\n});\n" + } + }, + { + "framework": "vue", + "displayName": "Vue 3 (Vite)", + "tier": 1, + "sandpackTemplate": "vue-ts", + "container": null, + "htWrappers": [ + "@handsontable/vue3" + ], + "entry": "/src/main.ts", + "htmlEntry": "/index.html", + "devCommand": null, + "buildCommand": "vite build", + "outputDir": "dist", + "outputGlob": null, + "staticExport": false, + "spaMode": false, + "port": null, + "installCommand": "npm install", + "htCoreRange": "18.0.0", + "fileCount": 26, + "assets": [ + "public/favicon.ico" + ], + "skipped": [], + "files": { + "/.gitignore": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\nlerna-debug.log*\n\nnode_modules\ndist\ndist-ssr\n*.local\n\n# Editor directories and files\n.vscode/*\n!.vscode/extensions.json\n.idea\n.DS_Store\n*.suo\n*.ntvs*\n*.njsproj\n*.sln\n*.sw?\n", + "/README.md": "# vue3-ts-example\n\nThis template should help get you started developing with Vue 3 in Vite.\n\n## Recommended IDE Setup\n\n[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur).\n\n## Type Support for `.vue` Imports in TS\n\nTypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) to make the TypeScript language service aware of `.vue` types.\n\n## Customize configuration\n\nSee [Vite Configuration Reference](https://vitejs.dev/config/).\n\n## Project Setup\n\n```sh\nnpm install\n```\n\n### Compile and Hot-Reload for Development\n\n```sh\nnpm run dev\n```\n\n### Type-Check, Compile and Minify for Production\n\n```sh\nnpm run build\n```\n", + "/env.d.ts": "/// \n", + "/index.html": "\n\n \n \n \n \n Vite App\n \n \n
\n \n \n\n", + "/package.json": "{\n \"name\": \"vue3-ts-example\",\n \"version\": \"0.0.0\",\n \"private\": true,\n \"type\": \"module\",\n \"scripts\": {\n \"start\": \"vite\",\n \"dev\": \"vite\",\n \"build\": \"run-p type-check \\\"build-only {@}\\\" --\",\n \"preview\": \"vite preview\",\n \"build-only\": \"vite build\",\n \"type-check\": \"vue-tsc --build --force\"\n },\n \"dependencies\": {\n \"vue\": \"^3.5.39\",\n \"@handsontable/vue3\": \"18.0.0\",\n \"handsontable\": \"18.0.0\"\n },\n \"devDependencies\": {\n \"@tsconfig/node20\": \"^20.1.9\",\n \"@types/node\": \"^26.0.1\",\n \"@vitejs/plugin-vue\": \"^6.0.7\",\n \"@vue/tsconfig\": \"^0.9.1\",\n \"npm-run-all2\": \"^9.0.2\",\n \"typescript\": \"~6.0.3\",\n \"vite\": \"^8.1.1\",\n \"vue-tsc\": \"^3.3.5\"\n },\n \"config\": {\n \"codesandbox\": {\n \"templateId\": \"53p5q2\"\n }\n }\n}\n", + "/src/App.vue": "\n\n\n", + "/src/assets/base.css": "", + "/src/assets/logo.svg": "\n", + "/src/assets/main.css": "", + "/src/components/DataGrid.vue": "\n\n\n\n\n", + "/src/components/HelloWorld.vue": "\n\n\n\n\n", + "/src/components/TheWelcome.vue": "\n\n\n", + "/src/components/WelcomeItem.vue": "\n\n\n", + "/src/components/icons/IconCommunity.vue": "\n", + "/src/components/icons/IconDocumentation.vue": "\n", + "/src/components/icons/IconEcosystem.vue": "\n", + "/src/components/icons/IconSupport.vue": "\n", + "/src/components/icons/IconTooling.vue": "\n\n", + "/src/constants.ts": "export const data = [\n {\n checked: false,\n companyName: \"Tagcat\",\n country: \"United Kingdom\",\n name: \"Classic Vest\",\n sellDate: \"2020-10-11\",\n orderId: \"01-2331942\",\n inStock: true,\n quantity: \"172\",\n progress: 20,\n rating: 2\n },\n {\n checked: true,\n companyName: \"Zoomzone\",\n country: \"Indonesia\",\n name: \"Cycling Cap\",\n sellDate: \"2020-05-03\",\n orderId: \"88-2768633\",\n inStock: true,\n quantity: \"188\",\n progress: 60,\n rating: 2\n },\n {\n checked: true,\n companyName: \"Meeveo\",\n country: \"United States\",\n name: \"Full-Finger Gloves\",\n sellDate: \"2020-03-27\",\n orderId: \"51-6775945\",\n inStock: true,\n quantity: \"162\",\n progress: 10,\n rating: 3\n },\n {\n checked: false,\n companyName: \"Buzzdog\",\n country: \"Philippines\",\n name: \"HL Mountain Frame\",\n sellDate: \"2020-08-29\",\n orderId: \"44-4028109\",\n inStock: true,\n quantity: \"133\",\n progress: 70,\n rating: 1\n },\n {\n checked: true,\n companyName: \"Katz\",\n country: \"India\",\n name: \"Half-Finger Gloves\",\n sellDate: \"2020-10-02\",\n orderId: \"08-2758492\",\n inStock: true,\n quantity: \"87\",\n progress: 10,\n rating: 3\n },\n {\n checked: false,\n companyName: \"Jaxbean\",\n country: \"China\",\n name: \"HL Road Frame\",\n sellDate: \"2020-09-28\",\n orderId: \"84-3557705\",\n inStock: false,\n quantity: \"26\",\n progress: 80,\n rating: 1\n },\n {\n checked: false,\n companyName: \"Wikido\",\n country: \"Brazil\",\n name: \"HL Touring Frame\",\n sellDate: \"2020-06-24\",\n orderId: \"20-9397637\",\n inStock: false,\n quantity: \"110\",\n progress: 40,\n rating: 1\n },\n {\n checked: false,\n companyName: \"Browsedrive\",\n country: \"United States\",\n name: \"LL Mountain Frame\",\n sellDate: \"2020-03-13\",\n orderId: \"36-0079556\",\n inStock: true,\n quantity: \"50\",\n progress: 40,\n rating: 4\n },\n {\n checked: false,\n companyName: \"Twinder\",\n country: \"United Kingdom\",\n name: \"LL Road Frame\",\n sellDate: \"2020-04-06\",\n orderId: \"41-1489542\",\n inStock: false,\n quantity: \"160\",\n progress: 60,\n rating: 1\n },\n {\n checked: false,\n companyName: \"Jetwire\",\n country: \"China\",\n name: \"LL Touring Frame\",\n sellDate: \"2020-02-01\",\n orderId: \"37-1531629\",\n inStock: true,\n quantity: \"30\",\n progress: 80,\n rating: 5\n },\n {\n checked: false,\n companyName: \"Chatterpoint\",\n country: \"China\",\n name: \"Long-Sleeve Logo Jersey\",\n sellDate: \"2020-07-14\",\n orderId: \"25-5083429\",\n inStock: true,\n quantity: \"39\",\n progress: 70,\n rating: 2\n },\n {\n checked: false,\n companyName: \"Twinder\",\n country: \"Egypt\",\n name: \"Men's Bib-Shorts\",\n sellDate: \"2020-08-31\",\n orderId: \"04-4281278\",\n inStock: false,\n quantity: \"96\",\n progress: 60,\n rating: 1\n },\n {\n checked: false,\n companyName: \"Midel\",\n country: \"United States\",\n name: \"Men's Sports Shorts\",\n sellDate: \"2020-06-27\",\n orderId: \"55-1711908\",\n inStock: true,\n quantity: \"108\",\n progress: 100,\n rating: 3\n },\n {\n checked: false,\n companyName: \"Yodo\",\n country: \"India\",\n name: \"ML Mountain Frame\",\n sellDate: \"2020-03-16\",\n orderId: \"58-8360815\",\n inStock: false,\n quantity: \"46\",\n progress: 10,\n rating: 1\n },\n {\n checked: false,\n companyName: \"Camido\",\n country: \"Russia\",\n name: \"ML Mountain Frame-W\",\n sellDate: \"2020-09-13\",\n orderId: \"10-3786104\",\n inStock: true,\n quantity: \"97\",\n progress: 80,\n rating: 3\n },\n {\n checked: false,\n companyName: \"Eire\",\n country: \"Thailand\",\n name: \"ML Road Frame\",\n sellDate: \"2020-04-10\",\n orderId: \"45-1186054\",\n inStock: true,\n quantity: \"161\",\n progress: 10,\n rating: 4\n },\n {\n checked: false,\n companyName: \"Vinte\",\n country: \"United Kingdom\",\n name: \"ML Road Frame-W\",\n sellDate: \"2020-01-22\",\n orderId: \"62-6202742\",\n inStock: true,\n quantity: \"58\",\n progress: 40,\n rating: 3\n },\n {\n checked: false,\n companyName: \"Twitterlist\",\n country: \"China\",\n name: \"Mountain Bike Socks\",\n sellDate: \"2020-11-09\",\n orderId: \"88-9646223\",\n inStock: true,\n quantity: \"92\",\n progress: 80,\n rating: 3\n },\n {\n checked: false,\n companyName: \"Eidel\",\n country: \"Bangladesh\",\n name: \"Mountain-100\",\n sellDate: \"2020-09-19\",\n orderId: \"45-5588112\",\n inStock: true,\n quantity: \"5\",\n progress: 60,\n rating: 5\n },\n {\n checked: false,\n companyName: \"Trunyx\",\n country: \"Nigeria\",\n name: \"Mountain-200\",\n sellDate: \"2020-03-09\",\n orderId: \"66-6271819\",\n inStock: true,\n quantity: \"158\",\n progress: 40,\n rating: 1\n },\n {\n checked: false,\n companyName: \"Katz\",\n country: \"Turkey\",\n name: \"Mountain-300\",\n sellDate: \"2020-03-05\",\n orderId: \"38-9245023\",\n inStock: false,\n quantity: \"121\",\n progress: 50,\n rating: 4\n },\n {\n checked: false,\n companyName: \"Kaymbo\",\n country: \"United States\",\n name: \"Mountain-400-W\",\n sellDate: \"2020-12-24\",\n orderId: \"44-5916927\",\n inStock: false,\n quantity: \"61\",\n progress: 50,\n rating: 4\n },\n {\n checked: false,\n companyName: \"Ozu\",\n country: \"Pakistan\",\n name: \"Mountain-500\",\n sellDate: \"2020-06-13\",\n orderId: \"31-5449914\",\n inStock: true,\n quantity: \"155\",\n progress: 20,\n rating: 2\n },\n {\n checked: false,\n companyName: \"Rhynyx\",\n country: \"India\",\n name: \"Racing Socks\",\n sellDate: \"2020-12-05\",\n orderId: \"19-9413869\",\n inStock: true,\n quantity: \"162\",\n progress: 20,\n rating: 4\n },\n {\n checked: false,\n companyName: \"Flashset\",\n country: \"Iran\",\n name: \"Road-150\",\n sellDate: \"2020-12-14\",\n orderId: \"25-9807605\",\n inStock: false,\n quantity: \"46\",\n progress: 70,\n rating: 1\n },\n {\n checked: false,\n companyName: \"Yata\",\n country: \"Congo (Kinshasa)\",\n name: \"Road-250\",\n sellDate: \"2020-06-12\",\n orderId: \"74-4291983\",\n inStock: true,\n quantity: \"47\",\n progress: 40,\n rating: 4\n },\n {\n checked: false,\n companyName: \"Brainlounge\",\n country: \"Vietnam\",\n name: \"Road-350-W\",\n sellDate: \"2020-03-10\",\n orderId: \"83-0980643\",\n inStock: true,\n quantity: \"104\",\n progress: 20,\n rating: 3\n },\n {\n checked: false,\n companyName: \"Babblestorm\",\n country: \"United States\",\n name: \"Road-450\",\n sellDate: \"2020-10-10\",\n orderId: \"19-2878430\",\n inStock: true,\n quantity: \"101\",\n progress: 60,\n rating: 4\n },\n {\n checked: false,\n companyName: \"Youspan\",\n country: \"Brazil\",\n name: \"Road-550-W\",\n sellDate: \"2020-12-16\",\n orderId: \"19-1838230\",\n inStock: true,\n quantity: \"150\",\n progress: 100,\n rating: 3\n },\n {\n checked: false,\n companyName: \"Nlounge\",\n country: \"China\",\n name: \"Road-650\",\n sellDate: \"2020-10-31\",\n orderId: \"32-2267938\",\n inStock: true,\n quantity: \"42\",\n progress: 40,\n rating: 2\n },\n {\n checked: false,\n companyName: \"Twinte\",\n country: \"India\",\n name: \"Road-750\",\n sellDate: \"2020-08-17\",\n orderId: \"79-2821972\",\n inStock: true,\n quantity: \"144\",\n progress: 90,\n rating: 3\n },\n {\n checked: false,\n companyName: \"Oyonder\",\n country: \"United Kingdom\",\n name: \"Short-Sleeve Classic Jersey\",\n sellDate: \"2020-12-04\",\n orderId: \"46-6597557\",\n inStock: true,\n quantity: \"195\",\n progress: 40,\n rating: 1\n },\n {\n checked: false,\n companyName: \"Gigabox\",\n country: \"Pakistan\",\n name: \"Sport-100\",\n sellDate: \"2020-02-03\",\n orderId: \"15-1793960\",\n inStock: true,\n quantity: \"199\",\n progress: 40,\n rating: 4\n },\n {\n checked: false,\n companyName: \"Livetube\",\n country: \"France\",\n name: \"Touring-1000\",\n sellDate: \"2020-05-16\",\n orderId: \"86-0811003\",\n inStock: true,\n quantity: \"110\",\n progress: 40,\n rating: 5\n },\n {\n checked: false,\n companyName: \"Voomm\",\n country: \"United Kingdom\",\n name: \"Touring-2000\",\n sellDate: \"2020-07-15\",\n orderId: \"95-3068680\",\n inStock: true,\n quantity: \"51\",\n progress: 40,\n rating: 4\n },\n {\n checked: false,\n companyName: \"Voonyx\",\n country: \"China\",\n name: \"Touring-3000\",\n sellDate: \"2020-11-27\",\n orderId: \"35-3085360\",\n inStock: false,\n quantity: \"69\",\n progress: 20,\n rating: 5\n },\n {\n checked: false,\n companyName: \"Zoombeat\",\n country: \"United States\",\n name: \"Women's Mountain Shorts\",\n sellDate: \"2020-11-03\",\n orderId: \"56-8673088\",\n inStock: false,\n quantity: \"53\",\n progress: 20,\n rating: 3\n },\n {\n checked: false,\n companyName: \"Roomm\",\n country: \"China\",\n name: \"Women's Tights\",\n sellDate: \"2020-03-16\",\n orderId: \"76-0085918\",\n inStock: true,\n quantity: \"168\",\n progress: 10,\n rating: 1\n },\n {\n checked: false,\n companyName: \"Leenti\",\n country: \"China\",\n name: \"Mountain-400\",\n sellDate: \"2020-05-16\",\n orderId: \"03-0893276\",\n inStock: false,\n quantity: \"58\",\n progress: 10,\n rating: 4\n },\n {\n checked: false,\n companyName: \"Jetpulse\",\n country: \"United States\",\n name: \"Road-550\",\n sellDate: \"2020-02-08\",\n orderId: \"79-9013306\",\n inStock: true,\n quantity: \"152\",\n progress: 90,\n rating: 3\n },\n {\n checked: false,\n companyName: \"Katz\",\n country: \"Peru\",\n name: \"Road-350\",\n sellDate: \"2020-02-15\",\n orderId: \"55-7799920\",\n inStock: true,\n quantity: \"66\",\n progress: 40,\n rating: 2\n },\n {\n checked: false,\n companyName: \"Cogidoo\",\n country: \"India\",\n name: \"LL Mountain Front Wheel\",\n sellDate: \"2020-06-04\",\n orderId: \"07-0881122\",\n inStock: false,\n quantity: \"112\",\n progress: 90,\n rating: 2\n },\n {\n checked: false,\n companyName: \"Divavu\",\n country: \"Colombia\",\n name: \"Touring Rear Wheel\",\n sellDate: \"2020-02-24\",\n orderId: \"58-6157387\",\n inStock: true,\n quantity: \"50\",\n progress: 100,\n rating: 4\n },\n {\n checked: false,\n companyName: \"Mydeo\",\n country: \"China\",\n name: \"Touring Front Wheel\",\n sellDate: \"2020-12-07\",\n orderId: \"12-2810010\",\n inStock: false,\n quantity: \"31\",\n progress: 30,\n rating: 5\n },\n {\n checked: false,\n companyName: \"Browsebug\",\n country: \"Japan\",\n name: \"ML Mountain Front Wheel\",\n sellDate: \"2020-01-14\",\n orderId: \"64-9249984\",\n inStock: true,\n quantity: \"132\",\n progress: 50,\n rating: 5\n },\n {\n checked: false,\n companyName: \"Layo\",\n country: \"China\",\n name: \"HL Mountain Front Wheel\",\n sellDate: \"2020-04-24\",\n orderId: \"45-0739652\",\n inStock: true,\n quantity: \"45\",\n progress: 10,\n rating: 5\n },\n {\n checked: false,\n companyName: \"Snaptags\",\n country: \"United Kingdom\",\n name: \"LL Touring Handlebars\",\n sellDate: \"2020-08-06\",\n orderId: \"09-5712761\",\n inStock: true,\n quantity: \"197\",\n progress: 40,\n rating: 2\n },\n {\n checked: false,\n companyName: \"Cogilith\",\n country: \"China\",\n name: \"HL Touring Handlebars\",\n sellDate: \"2020-05-31\",\n orderId: \"01-7345008\",\n inStock: true,\n quantity: \"190\",\n progress: 40,\n rating: 3\n },\n {\n checked: false,\n companyName: \"Reallinks\",\n country: \"United Kingdom\",\n name: \"LL Road Front Wheel\",\n sellDate: \"2020-05-14\",\n orderId: \"62-1065350\",\n inStock: true,\n quantity: \"184\",\n progress: 30,\n rating: 4\n },\n {\n checked: false,\n companyName: \"Quaxo\",\n country: \"United States\",\n name: \"ML Road Front Wheel\",\n sellDate: \"2020-03-23\",\n orderId: \"44-7241323\",\n inStock: true,\n quantity: \"169\",\n progress: 30,\n rating: 4\n },\n {\n checked: false,\n companyName: \"Devify\",\n country: \"China\",\n name: \"HL Road Front Wheel\",\n sellDate: \"2020-12-12\",\n orderId: \"52-0295699\",\n inStock: false,\n quantity: \"152\",\n progress: 40,\n rating: 4\n },\n {\n checked: false,\n companyName: \"Youopia\",\n country: \"Angola\",\n name: \"LL Mountain Handlebars\",\n sellDate: \"2020-04-01\",\n orderId: \"52-2650922\",\n inStock: false,\n quantity: \"182\",\n progress: 60,\n rating: 4\n },\n {\n checked: false,\n companyName: \"Ainyx\",\n country: \"China\",\n name: \"Touring Pedal\",\n sellDate: \"2020-02-27\",\n orderId: \"48-3618525\",\n inStock: true,\n quantity: \"141\",\n progress: 60,\n rating: 1\n },\n {\n checked: false,\n companyName: \"Browsetype\",\n country: \"Malaysia\",\n name: \"ML Mountain Handlebars\",\n sellDate: \"2020-04-28\",\n orderId: \"51-8893923\",\n inStock: true,\n quantity: \"169\",\n progress: 70,\n rating: 1\n },\n {\n checked: false,\n companyName: \"Muxo\",\n country: \"China\",\n name: \"HL Mountain Handlebars\",\n sellDate: \"2020-08-22\",\n orderId: \"68-5911361\",\n inStock: false,\n quantity: \"39\",\n progress: 70,\n rating: 1\n },\n {\n checked: false,\n companyName: \"Bubbletube\",\n country: \"China\",\n name: \"LL Road Handlebars\",\n sellDate: \"2020-10-04\",\n orderId: \"41-5880042\",\n inStock: true,\n quantity: \"71\",\n progress: 80,\n rating: 3\n },\n {\n checked: false,\n companyName: \"Fadeo\",\n country: \"Vietnam\",\n name: \"ML Road Handlebars\",\n sellDate: \"2020-04-23\",\n orderId: \"90-5913983\",\n inStock: true,\n quantity: \"148\",\n progress: 100,\n rating: 3\n },\n {\n checked: false,\n companyName: \"Yadel\",\n country: \"United Kingdom\",\n name: \"HL Road Handlebars\",\n sellDate: \"2020-04-18\",\n orderId: \"92-0960699\",\n inStock: true,\n quantity: \"116\",\n progress: 80,\n rating: 1\n },\n {\n checked: false,\n companyName: \"Blognation\",\n country: \"China\",\n name: \"LL Headset\",\n sellDate: \"2020-01-10\",\n orderId: \"06-9493898\",\n inStock: true,\n quantity: \"96\",\n progress: 100,\n rating: 1\n },\n {\n checked: false,\n companyName: \"Devpoint\",\n country: \"China\",\n name: \"ML Headset\",\n sellDate: \"2020-12-25\",\n orderId: \"69-5878565\",\n inStock: true,\n quantity: \"35\",\n progress: 40,\n rating: 2\n },\n {\n checked: false,\n companyName: \"Aibox\",\n country: \"United Kingdom\",\n name: \"HL Headset\",\n sellDate: \"2020-03-18\",\n orderId: \"13-1133017\",\n inStock: true,\n quantity: \"16\",\n progress: 80,\n rating: 2\n },\n {\n checked: false,\n companyName: \"Brightdog\",\n country: \"China\",\n name: \"LL Mountain Pedal\",\n sellDate: \"2020-09-11\",\n orderId: \"39-6530433\",\n inStock: true,\n quantity: \"194\",\n progress: 20,\n rating: 5\n },\n {\n checked: false,\n companyName: \"Gabcube\",\n country: \"Nigeria\",\n name: \"ML Mountain Pedal\",\n sellDate: \"2020-04-22\",\n orderId: \"96-6860388\",\n inStock: true,\n quantity: \"24\",\n progress: 10,\n rating: 3\n },\n {\n checked: false,\n companyName: \"Muxo\",\n country: \"China\",\n name: \"HL Mountain Pedal\",\n sellDate: \"2020-06-05\",\n orderId: \"30-0356137\",\n inStock: true,\n quantity: \"170\",\n progress: 40,\n rating: 4\n },\n {\n checked: false,\n companyName: \"Tambee\",\n country: \"China\",\n name: \"ML Touring Seat/Saddle\",\n sellDate: \"2020-02-22\",\n orderId: \"93-9058255\",\n inStock: true,\n quantity: \"184\",\n progress: 90,\n rating: 5\n },\n {\n checked: false,\n companyName: \"Cogilith\",\n country: \"India\",\n name: \"LL Touring Seat/Saddle\",\n sellDate: \"2020-04-06\",\n orderId: \"82-9268909\",\n inStock: false,\n quantity: \"153\",\n progress: 100,\n rating: 4\n },\n {\n checked: false,\n companyName: \"Dynabox\",\n country: \"Hong Kong\",\n name: \"HL Touring Seat/Saddle\",\n sellDate: \"2020-01-10\",\n orderId: \"20-6913815\",\n inStock: false,\n quantity: \"88\",\n progress: 100,\n rating: 1\n },\n {\n checked: false,\n companyName: \"Shuffledrive\",\n country: \"Sudan\",\n name: \"LL Road Pedal\",\n sellDate: \"2020-09-16\",\n orderId: \"08-8238817\",\n inStock: true,\n quantity: \"57\",\n progress: 90,\n rating: 2\n },\n {\n checked: false,\n companyName: \"Fivechat\",\n country: \"China\",\n name: \"ML Road Pedal\",\n sellDate: \"2020-08-26\",\n orderId: \"44-7370350\",\n inStock: false,\n quantity: \"62\",\n progress: 40,\n rating: 1\n },\n {\n checked: false,\n companyName: \"Meembee\",\n country: \"United States\",\n name: \"HL Road Pedal\",\n sellDate: \"2020-12-27\",\n orderId: \"01-3525949\",\n inStock: true,\n quantity: \"123\",\n progress: 20,\n rating: 4\n },\n {\n checked: false,\n companyName: \"Dynazzy\",\n country: \"United Kingdom\",\n name: \"LL Mountain Seat/Saddle 1\",\n sellDate: \"2020-12-15\",\n orderId: \"04-2414623\",\n inStock: true,\n quantity: \"77\",\n progress: 100,\n rating: 5\n },\n {\n checked: false,\n companyName: \"Eare\",\n country: \"China\",\n name: \"ML Mountain Seat/Saddle 1\",\n sellDate: \"2020-04-04\",\n orderId: \"15-1917509\",\n inStock: false,\n quantity: \"199\",\n progress: 90,\n rating: 4\n },\n {\n checked: false,\n companyName: \"Yozio\",\n country: \"China\",\n name: \"HL Mountain Seat/Saddle 1\",\n sellDate: \"2020-03-15\",\n orderId: \"06-2526845\",\n inStock: true,\n quantity: \"149\",\n progress: 80,\n rating: 2\n },\n {\n checked: false,\n companyName: \"Quinu\",\n country: \"Xi'an\",\n name: \"425-777-7829\",\n sellDate: \"2020-02-22\",\n orderId: \"83-1713558\",\n inStock: false,\n quantity: \"191\",\n progress: 90,\n rating: 5\n },\n {\n checked: false,\n companyName: \"Jazzy\",\n country: \"United Kingdom\",\n name: \"ML Road Seat/Saddle 1\",\n sellDate: \"2020-08-07\",\n orderId: \"00-8892524\",\n inStock: true,\n quantity: \"150\",\n progress: 100,\n rating: 2\n },\n {\n checked: false,\n companyName: \"Thoughtsphere\",\n country: \"China\",\n name: \"HL Road Seat/Saddle 1\",\n sellDate: \"2020-11-28\",\n orderId: \"39-5538991\",\n inStock: true,\n quantity: \"130\",\n progress: 70,\n rating: 3\n },\n {\n checked: false,\n companyName: \"Leenti\",\n country: \"China\",\n name: \"ML Road Rear Wheel\",\n sellDate: \"2020-12-29\",\n orderId: \"06-9002973\",\n inStock: true,\n quantity: \"179\",\n progress: 10,\n rating: 2\n },\n {\n checked: false,\n companyName: \"Quaxo\",\n country: \"United Kingdom\",\n name: \"HL Road Rear Wheel\",\n sellDate: \"2020-09-06\",\n orderId: \"73-6104901\",\n inStock: true,\n quantity: \"98\",\n progress: 50,\n rating: 3\n },\n {\n checked: false,\n companyName: \"Tanoodle\",\n country: \"Chile\",\n name: \"LL Mountain Seat/Saddle 2\",\n sellDate: \"2020-05-24\",\n orderId: \"68-7384479\",\n inStock: true,\n quantity: \"175\",\n progress: 20,\n rating: 3\n },\n {\n checked: false,\n companyName: \"Feednation\",\n country: \"China\",\n name: \"ML Mountain Seat/Saddle 2\",\n sellDate: \"2020-11-21\",\n orderId: \"26-7757763\",\n inStock: true,\n quantity: \"11\",\n progress: 10,\n rating: 3\n },\n {\n checked: false,\n companyName: \"Kayveo\",\n country: \"China\",\n name: \"HL Mountain Seat/Saddle 2\",\n sellDate: \"2020-06-21\",\n orderId: \"07-4873562\",\n inStock: false,\n quantity: \"184\",\n progress: 70,\n rating: 4\n },\n {\n checked: false,\n companyName: \"Meevee\",\n country: \"Saudi Arabia\",\n name: \"LL Road Seat/Saddle 1\",\n sellDate: \"2020-11-16\",\n orderId: \"46-5819554\",\n inStock: false,\n quantity: \"27\",\n progress: 90,\n rating: 3\n },\n {\n checked: false,\n companyName: \"Twitterworks\",\n country: \"China\",\n name: \"ML Road Seat/Saddle 2\",\n sellDate: \"2020-04-19\",\n orderId: \"01-2666826\",\n inStock: true,\n quantity: \"186\",\n progress: 30,\n rating: 2\n },\n {\n checked: false,\n companyName: \"Wikizz\",\n country: \"Tanzania\",\n name: \"HL Road Seat/Saddle 2\",\n sellDate: \"2020-03-08\",\n orderId: \"54-7090503\",\n inStock: true,\n quantity: \"20\",\n progress: 30,\n rating: 3\n },\n {\n checked: false,\n companyName: \"Yoveo\",\n country: \"United States\",\n name: \"LL Mountain Tire\",\n sellDate: \"2020-10-14\",\n orderId: \"78-7658520\",\n inStock: false,\n quantity: \"153\",\n progress: 20,\n rating: 1\n },\n {\n checked: false,\n companyName: \"Yakidoo\",\n country: \"China\",\n name: \"ML Mountain Tire\",\n sellDate: \"2020-10-12\",\n orderId: \"23-9926318\",\n inStock: true,\n quantity: \"161\",\n progress: 80,\n rating: 5\n },\n {\n checked: false,\n companyName: \"Oyope\",\n country: \"China\",\n name: \"HL Mountain Tire\",\n sellDate: \"2020-09-20\",\n orderId: \"20-0179517\",\n inStock: true,\n quantity: \"98\",\n progress: 100,\n rating: 5\n },\n {\n checked: false,\n companyName: \"Skipstorm\",\n country: \"United States\",\n name: \"LL Road Tire\",\n sellDate: \"2020-10-01\",\n orderId: \"02-9543343\",\n inStock: true,\n quantity: \"30\",\n progress: 70,\n rating: 3\n },\n {\n checked: false,\n companyName: \"Minyx\",\n country: \"United States\",\n name: \"ML Road Tire\",\n sellDate: \"2020-07-07\",\n orderId: \"98-3938169\",\n inStock: true,\n quantity: \"73\",\n progress: 100,\n rating: 2\n },\n {\n checked: false,\n companyName: \"Miboo\",\n country: \"China\",\n name: \"HL Road Tire\",\n sellDate: \"2020-07-25\",\n orderId: \"68-5197934\",\n inStock: true,\n quantity: \"158\",\n progress: 90,\n rating: 1\n },\n {\n checked: false,\n companyName: \"Realfire\",\n country: \"United States\",\n name: \"Touring Tire\",\n sellDate: \"2020-08-27\",\n orderId: \"39-8260460\",\n inStock: true,\n quantity: \"122\",\n progress: 50,\n rating: 2\n },\n {\n checked: false,\n companyName: \"Shufflester\",\n country: \"China\",\n name: \"Mountain Tire Tube\",\n sellDate: \"2020-06-08\",\n orderId: \"45-9776170\",\n inStock: true,\n quantity: \"33\",\n progress: 20,\n rating: 4\n },\n {\n checked: false,\n companyName: \"Ntag\",\n country: \"China\",\n name: \"Road Tire Tube\",\n sellDate: \"2020-12-06\",\n orderId: \"45-0858451\",\n inStock: true,\n quantity: \"107\",\n progress: 60,\n rating: 2\n },\n {\n checked: false,\n companyName: \"Jabberbean\",\n country: \"United States\",\n name: \"Touring Tire Tube\",\n sellDate: \"2020-04-26\",\n orderId: \"15-4247305\",\n inStock: true,\n quantity: \"15\",\n progress: 10,\n rating: 2\n },\n {\n checked: false,\n companyName: \"Thoughtblab\",\n country: \"China\",\n name: \"LL Bottom Bracket\",\n sellDate: \"2020-05-21\",\n orderId: \"15-8534931\",\n inStock: true,\n quantity: \"168\",\n progress: 50,\n rating: 2\n },\n {\n checked: false,\n companyName: \"Jabbertype\",\n country: \"China\",\n name: \"Classic Vest\",\n sellDate: \"2020-07-25\",\n orderId: \"23-1251557\",\n inStock: true,\n quantity: \"135\",\n progress: 40,\n rating: 2\n },\n {\n checked: false,\n companyName: \"Buzzshare\",\n country: \"United Kingdom\",\n name: \"Cycling Cap\",\n sellDate: \"2020-07-07\",\n orderId: \"86-5920601\",\n inStock: true,\n quantity: \"11\",\n progress: 10,\n rating: 4\n },\n {\n checked: false,\n companyName: \"Roodel\",\n country: \"United States\",\n name: \"Full-Finger Gloves\",\n sellDate: \"2020-01-13\",\n orderId: \"48-1055459\",\n inStock: true,\n quantity: \"41\",\n progress: 60,\n rating: 4\n },\n {\n checked: false,\n companyName: \"Zoovu\",\n country: \"China\",\n name: \"Half-Finger Gloves\",\n sellDate: \"2020-06-03\",\n orderId: \"12-7842022\",\n inStock: true,\n quantity: \"144\",\n progress: 60,\n rating: 1\n },\n {\n checked: false,\n companyName: \"Photofeed\",\n country: \"China\",\n name: \"HL Mountain Frame\",\n sellDate: \"2020-07-14\",\n orderId: \"94-5088099\",\n inStock: true,\n quantity: \"106\",\n progress: 10,\n rating: 4\n }\n];\n\nexport const ODD_ROW_CLASS = \"odd\";\n\nexport const MESSAGE = {\n BAD_VALUE: \"#bad-value#\"\n};\n", + "/src/hooks-callbacks.ts": "import Handsontable from \"handsontable\";\nimport type { RowObject } from \"handsontable/common\";\n\nimport { ODD_ROW_CLASS } from \"./constants\";\n\ntype AddClassesToRows = (\n TD: HTMLTableCellElement,\n row: number,\n column: number,\n prop: number | string,\n value: any,\n cellProperties: Handsontable.CellProperties\n) => void;\n\nexport const addClassesToRows: AddClassesToRows = (\n TD,\n row,\n column,\n prop,\n value,\n cellProperties\n) => {\n // Adding classes to `TR` just while rendering first visible `TD` element\n if (column !== 0) {\n return;\n }\n\n const parentElement = TD.parentElement;\n\n if (parentElement === null) {\n return;\n }\n\n const rowData = cellProperties.instance.getSourceDataAtRow(row) as RowObject;\n\n // Add class to odd TRs\n if (row % 2 === 0) {\n Handsontable.dom.addClass(parentElement, ODD_ROW_CLASS);\n } else {\n Handsontable.dom.removeClass(parentElement, ODD_ROW_CLASS);\n }\n};\n\nexport function alignHeaders(\n this: Handsontable,\n column: number,\n TH: HTMLTableCellElement\n) {\n if (column < 0) {\n return;\n }\n\n if (!TH.firstChild) {\n return;\n }\n\n const alignmentClass = this.isRtl() ? \"htRight\" : \"htLeft\";\n Handsontable.dom.addClass(TH.firstChild as HTMLElement, alignmentClass);\n}\n\n", + "/src/main.ts": "import './assets/main.css'\n\nimport { createApp } from 'vue'\nimport App from './App.vue'\n\ncreateApp(App).mount('#app')\n", + "/src/utils.ts": "export function getRangeValue(\n value: number,\n minAllowedValue: number,\n maxAllowedValue: number\n): number {\n if (value < minAllowedValue || !value) {\n return minAllowedValue;\n }\n\n if (value > maxAllowedValue) {\n return maxAllowedValue;\n }\n\n return value;\n}\n", + "/tsconfig.app.json": "{\n \"extends\": \"@vue/tsconfig/tsconfig.dom.json\",\n \"include\": [\"env.d.ts\", \"src/**/*\", \"src/**/*.vue\"],\n \"exclude\": [\"src/**/__tests__/*\"],\n \"compilerOptions\": {\n \"composite\": true,\n \"tsBuildInfoFile\": \"./node_modules/.tmp/tsconfig.app.tsbuildinfo\",\n\n \"baseUrl\": \".\",\n \"paths\": {\n \"@/*\": [\"./src/*\"]\n }\n }\n}\n", + "/tsconfig.json": "{\n \"files\": [],\n \"references\": [\n {\n \"path\": \"./tsconfig.node.json\"\n },\n {\n \"path\": \"./tsconfig.app.json\"\n }\n ]\n}\n", + "/tsconfig.node.json": "{\n \"extends\": \"@tsconfig/node20/tsconfig.json\",\n \"include\": [\n \"vite.config.*\",\n \"vitest.config.*\",\n \"cypress.config.*\",\n \"nightwatch.conf.*\",\n \"playwright.config.*\"\n ],\n \"compilerOptions\": {\n \"composite\": true,\n \"noEmit\": true,\n \"tsBuildInfoFile\": \"./node_modules/.tmp/tsconfig.node.tsbuildinfo\",\n\n \"module\": \"ESNext\",\n \"moduleResolution\": \"Bundler\",\n \"types\": [\"node\"]\n }\n}\n", + "/vite.config.ts": "import { fileURLToPath, URL } from 'node:url'\n\nimport { defineConfig } from 'vite'\nimport vue from '@vitejs/plugin-vue'\n\n// https://vitejs.dev/config/\nexport default defineConfig({\n plugins: [vue()],\n resolve: {\n alias: {\n '@': fileURLToPath(new URL('./src', import.meta.url)),\n },\n },\n server: {\n allowedHosts: true,\n },\n})\n" + } + }, + { + "framework": "angular", + "displayName": "Angular", + "tier": 2, + "sandpackTemplate": null, + "container": "angular", + "htWrappers": [ + "@handsontable/angular-wrapper" + ], + "entry": "/src/main.ts", + "htmlEntry": "/src/index.html", + "devCommand": "npm run start -- --host 0.0.0.0 --disable-host-check --port 3000", + "buildCommand": "ng build", + "outputDir": "dist", + "outputGlob": "dist/*/browser", + "staticExport": false, + "spaMode": false, + "port": 3000, + "installCommand": "npm install --legacy-peer-deps", + "htCoreRange": "latest", + "fileCount": 17, + "assets": [ + "src/favicon.ico" + ], + "skipped": [], + "files": { + "/.gitignore": "node_modules\ndist\n.angular\n.env\n.env.local\n.env.development.local\n.env.test.local\n.env.production.local\n.env.development\n.env.test\n.env.production\n.env.local\n.env.development.local\n.env.test.local\n.env.production.local", + "/.npmrc": "legacy-peer-deps=true\n", + "/LICENSE.txt": "Copyright (c) HANDSONCODE sp. z o. o.\n\nHANDSONTABLE is a software distributed by HANDSONCODE sp. z o. o.,\na Polish corporation, based in Gdynia, Poland, at 96/98 Aleja Zwycięstwa,\nregistered with the National Court Register under number 538651,\nEU tax ID number: PL5862294002, share capital: PLN 62,800.00.\n\nThis software is protected by applicable copyright laws, including\ninternational treaties, and dual-licensed – depending on whether\nyour use is intended for or may result in commercial advantage\nor monetary compensation (commercial purposes), or not.\n\nIf your use involves only such purposes as research, private study,\nevaluation and the like, you agree to be bound by the terms included\nin the \"handsontable-non-commercial-license.pdf\" file, available\nin the main directory of this software repository.\n\nBy installing, copying, or otherwise using this software for\ncommercial purposes, you agree to be bound by the terms included\nin the \"handsontable-general-terms.pdf\" file, available in the main\ndirectory of this software repository.\n\nHANDSONCODE PROVIDES THIS SOFTWARE ON AN \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND. IN NO EVENT\nAND UNDER NO LEGAL THEORY, SHALL HANDSONCODE BE LIABLE\nTO YOU FOR DAMAGES, INCLUDING ANY DIRECT, INDIRECT, SPECIAL,\nINCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER ARISING\nFROM USE OR INABILITY TO USE THIS SOFTWARE.\n", + "/README.md": "# Demo\n\n## Description\n\nA general purpose demo showcasing some of the most popular features of Handsontable in Angular.\n\n## Installation\n\nInstall all necessary dependencies from npm with `npm install`.\n\n## Development\n\nTo start local development server use `npm run start`. You can then visit http://localhost:8080 to view the project.\n\n## Testing\n\nTo run tests for this project, first launch the development server with `npm run start`. Once the server is running, execute `npm run test` to run the test specs.\n\n## Forking\n\nThere are two ways to fork this project in Handsontable's monorepo:\n\n1. Fork the entire repository.\n2. Copy the example to a new repository. Detailed instructions are available [here](../../../../README.md#copying-an-example-to-a-separate-repo) in the section _Copying an example to a separate repo_.\n\n## License\n\nHandsontable is a commercial software with two licenses available:\n\n- Free for non-commercial purposes such as teaching, academic research, and evaluation. [Read it here](https://github.com/handsontable/handsontable/blob/master/handsontable-non-commercial-license.pdf).\n- Commercial license with support and maintenance included. See [pricing plans](https://handsontable.com/pricing).\n\n## Get help\n\nJoin our [community](https://github.com/handsontable/handsontable/discussions) to get help, share ideas, and contribute to the ongoing development of Handsontable!\n\nCommercial license holders with an active support plan can contact our [Support Team](https://handsontable.com/contact?category=technical_support).\n", + "/angular.json": "{\n \"$schema\": \"./node_modules/@angular/cli/lib/config/schema.json\",\n \"version\": 1,\n \"newProjectRoot\": \"projects\",\n \"projects\": {\n \"sandbox\": {\n \"projectType\": \"application\",\n \"schematics\": {},\n \"root\": \"\",\n \"sourceRoot\": \"src\",\n \"prefix\": \"app\",\n \"architect\": {\n \"build\": {\n \"builder\": \"@angular-devkit/build-angular:application\",\n \"options\": {\n \"outputPath\": {\n \"base\": \"dist/sandbox\"\n },\n \"index\": \"src/index.html\",\n \"browser\": \"src/main.ts\",\n \"polyfills\": [\n \"zone.js\"\n ],\n \"tsConfig\": \"tsconfig.app.json\",\n \"assets\": [\n \"src/favicon.ico\",\n \"src/assets\"\n ],\n \"styles\": [\n \"node_modules/handsontable/styles/handsontable.css\",\n \"node_modules/handsontable/styles/ht-theme-main.css\"\n ],\n \"scripts\": [],\n \"allowedCommonJsDependencies\": [\n \"core-js\",\n \"@handsontable/pikaday\",\n \"numbro\"\n ]\n },\n \"configurations\": {\n \"production\": {\n \"budgets\": [\n {\n \"type\": \"initial\",\n \"maximumWarning\": \"500kb\",\n \"maximumError\": \"1mb\"\n },\n {\n \"type\": \"anyComponentStyle\",\n \"maximumWarning\": \"2kb\",\n \"maximumError\": \"4kb\"\n }\n ],\n \"outputHashing\": \"all\"\n },\n \"development\": {\n \"optimization\": false,\n \"extractLicenses\": false,\n \"sourceMap\": true,\n \"namedChunks\": true\n }\n },\n \"defaultConfiguration\": \"production\"\n },\n \"serve\": {\n \"builder\": \"@angular-devkit/build-angular:dev-server\",\n \"configurations\": {\n \"production\": {\n \"buildTarget\": \"sandbox:build:production\"\n },\n \"development\": {\n \"buildTarget\": \"sandbox:build:development\"\n }\n },\n \"defaultConfiguration\": \"development\"\n },\n \"extract-i18n\": {\n \"builder\": \"@angular-devkit/build-angular:extract-i18n\",\n \"options\": {\n \"browserTarget\": \"sandbox:build\"\n }\n },\n \"test\": {\n \"builder\": \"@angular-devkit/build-angular:karma\",\n \"options\": {\n \"polyfills\": [\n \"zone.js\",\n \"zone.js/testing\"\n ],\n \"tsConfig\": \"tsconfig.spec.json\",\n \"assets\": [\n \"src/favicon.ico\",\n \"src/assets\"\n ],\n \"styles\": [\n \"src/styles.css\"\n ],\n \"scripts\": []\n }\n }\n }\n }\n },\n \"cli\": {\n \"analytics\": false\n }\n}\n", + "/package.json": "{\n \"name\": \"sandbox\",\n \"version\": \"0.0.0\",\n \"scripts\": {\n \"ng\": \"ng\",\n \"start\": \"ng serve --disable-host-check --port 3000\",\n \"build\": \"ng build\",\n \"watch\": \"ng build --watch --configuration development\",\n \"test\": \"ng test\"\n },\n \"private\": true,\n \"dependencies\": {\n \"@angular/animations\": \"^21.1.0\",\n \"@angular/common\": \"^21.1.0\",\n \"@angular/compiler\": \"^21.1.0\",\n \"@angular/core\": \"^21.1.0\",\n \"@angular/forms\": \"^21.1.0\",\n \"@angular/platform-browser\": \"^21.1.0\",\n \"@angular/platform-browser-dynamic\": \"^21.1.0\",\n \"@angular/router\": \"^21.1.0\",\n \"@handsontable/angular-wrapper\": \"latest\",\n \"handsontable\": \"latest\",\n \"rxjs\": \"~7.8.0\",\n \"tslib\": \"^2.6.2\",\n \"zone.js\": \"~0.15.0\"\n },\n \"devDependencies\": {\n \"@angular-devkit/build-angular\": \"^21.1.0\",\n \"@angular/cli\": \"^21.1.0\",\n \"@angular/compiler-cli\": \"^21.1.0\",\n \"@types/jasmine\": \"3.6.9\",\n \"@types/node\": \"12.20.7\",\n \"typescript\": \"~5.9.0\"\n },\n \"config\": {\n \"codesandbox\": {\n \"templateId\": \"chnr97\"\n }\n }\n}\n", + "/src/app/app.config.ts": "import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';\n\nexport const appConfig: ApplicationConfig = {\n providers: [provideZoneChangeDetection({ eventCoalescing: true })],\n};\n", + "/src/app/data-grid.component.html": "
\n \n
\n", + "/src/app/data-grid.component.ts": "import {\n Component,\n ViewEncapsulation,\n CUSTOM_ELEMENTS_SCHEMA,\n} from '@angular/core';\nimport { PredefinedMenuItemKey } from 'handsontable/plugins/contextMenu';\nimport { HotTableModule } from '@handsontable/angular-wrapper';\n\nimport { getData } from './utils/constants';\nimport { alignHeaders, addClassesToRows } from './utils/hooks-callbacks';\n\n@Component({\n encapsulation: ViewEncapsulation.None,\n selector: 'data-grid',\n standalone: true,\n templateUrl: './data-grid.component.html',\n //styleUrls: ['./data-grid.scss'],\n imports: [HotTableModule],\n schemas: [CUSTOM_ELEMENTS_SCHEMA],\n})\nexport class DataGridComponent {\n initialData = getData();\n gridSettings = {\n height: 450,\n colWidths: [180, 220, 140, 120, 120, 120, 140],\n colHeaders: [\n 'Company Name',\n 'Name',\n 'Sell date',\n 'In stock',\n 'Quantity',\n 'Order ID',\n 'Country',\n ],\n contextMenu: [\n 'cut',\n 'copy',\n '---------',\n 'row_above',\n 'row_below',\n 'remove_row',\n '---------',\n 'alignment',\n 'make_read_only',\n 'clear_column',\n ] as PredefinedMenuItemKey[],\n dropdownMenu: true,\n hiddenColumns: {\n indicators: true,\n },\n multiColumnSorting: true,\n filters: true,\n rowHeaders: true,\n headerClassName: 'htLeft',\n beforeRenderer: addClassesToRows,\n manualRowMove: true,\n autoWrapRow: true,\n autoWrapCol: true,\n autoRowSize: true,\n manualRowResize: true,\n manualColumnResize: true,\n navigableHeaders: true,\n imeFastEdit: true,\n columns: [\n { data: 1 },\n { data: 3 },\n { data: 4, type: 'date', allowInvalid: false },\n { data: 6, type: 'checkbox', className: 'htCenter' },\n { data: 7, type: 'numeric' },\n { data: 5 },\n { data: 2 },\n ],\n themeName: 'ht-theme-main',\n licenseKey: 'non-commercial-and-evaluation',\n };\n}\n", + "/src/app/utils/constants.ts": "export const data = [\n [\n false,\n 'Tagcat',\n 'United Kingdom',\n 'Classic Vest',\n '11/10/2020',\n '01-2331942',\n true,\n '172',\n 2,\n 2,\n ],\n [\n true,\n 'Zoomzone',\n 'Indonesia',\n 'Cycling Cap',\n '03/05/2020',\n '88-2768633',\n true,\n '188',\n 6,\n 2,\n ],\n [\n true,\n 'Meeveo',\n 'United States',\n 'Full-Finger Gloves',\n '27/03/2020',\n '51-6775945',\n true,\n '162',\n 1,\n 3,\n ],\n [\n false,\n 'Buzzdog',\n 'Philippines',\n 'HL Mountain Frame',\n '29/08/2020',\n '44-4028109',\n true,\n '133',\n 7,\n 1,\n ],\n [\n true,\n 'Katz',\n 'India',\n 'Half-Finger Gloves',\n '02/10/2020',\n '08-2758492',\n true,\n '87',\n 1,\n 3,\n ],\n [\n false,\n 'Jaxbean',\n 'China',\n 'HL Road Frame',\n '28/09/2020',\n '84-3557705',\n false,\n '26',\n 8,\n 1,\n ],\n [\n false,\n 'Wikido',\n 'Brazil',\n 'HL Touring Frame',\n '24/06/2020',\n '20-9397637',\n false,\n '110',\n 4,\n 1,\n ],\n [\n false,\n 'Browsedrive',\n 'United States',\n 'LL Mountain Frame',\n '13/03/2020',\n '36-0079556',\n true,\n '50',\n 4,\n 4,\n ],\n [\n false,\n 'Twinder',\n 'United Kingdom',\n 'LL Road Frame',\n '06/04/2020',\n '41-1489542',\n false,\n '160',\n 6,\n 1,\n ],\n [\n false,\n 'Jetwire',\n 'China',\n 'LL Touring Frame',\n '01/02/2020',\n '37-1531629',\n true,\n '30',\n 8,\n 5,\n ],\n [\n false,\n 'Chatterpoint',\n 'China',\n 'Long-Sleeve Logo Jersey',\n '14/07/2020',\n '25-5083429',\n true,\n '39',\n 7,\n 2,\n ],\n [\n false,\n 'Twinder',\n 'Egypt',\n \"Men's Bib-Shorts\",\n '31/08/2020',\n '04-4281278',\n false,\n '96',\n 6,\n 1,\n ],\n [\n false,\n 'Midel',\n 'United States',\n \"Men's Sports Shorts\",\n '27/06/2020',\n '55-1711908',\n true,\n '108',\n 10,\n 3,\n ],\n [\n false,\n 'Yodo',\n 'India',\n 'ML Mountain Frame',\n '16/03/2020',\n '58-8360815',\n false,\n '46',\n 1,\n 1,\n ],\n [\n false,\n 'Camido',\n 'Russia',\n 'ML Mountain Frame-W',\n '13/09/2020',\n '10-3786104',\n true,\n '97',\n 8,\n 3,\n ],\n [\n false,\n 'Eire',\n 'Thailand',\n 'ML Road Frame',\n '10/04/2020',\n '45-1186054',\n true,\n '161',\n 1,\n 4,\n ],\n [\n false,\n 'Vinte',\n 'United Kingdom',\n 'ML Road Frame-W',\n '22/01/2020',\n '62-6202742',\n true,\n '58',\n 4,\n 3,\n ],\n [\n false,\n 'Twitterlist',\n 'China',\n 'Mountain Bike Socks',\n '09/11/2020',\n '88-9646223',\n true,\n '92',\n 8,\n 3,\n ],\n [\n false,\n 'Eidel',\n 'Bangladesh',\n 'Mountain-100',\n '19/09/2020',\n '45-5588112',\n true,\n '5',\n 6,\n 5,\n ],\n [\n false,\n 'Trunyx',\n 'Nigeria',\n 'Mountain-200',\n '09/03/2020',\n '66-6271819',\n true,\n '158',\n 4,\n 1,\n ],\n [\n false,\n 'Katz',\n 'Turkey',\n 'Mountain-300',\n '05/03/2020',\n '38-9245023',\n false,\n '121',\n 5,\n 4,\n ],\n [\n false,\n 'Kaymbo',\n 'United States',\n 'Mountain-400-W',\n '24/12/2020',\n '44-5916927',\n false,\n '61',\n 5,\n 4,\n ],\n [\n false,\n 'Ozu',\n 'Pakistan',\n 'Mountain-500',\n '13/06/2020',\n '31-5449914',\n true,\n '155',\n 2,\n 2,\n ],\n [\n false,\n 'Rhynyx',\n 'India',\n 'Racing Socks',\n '05/12/2020',\n '19-9413869',\n true,\n '162',\n 2,\n 4,\n ],\n [\n false,\n 'Flashset',\n 'Iran',\n 'Road-150',\n '14/12/2020',\n '25-9807605',\n false,\n '46',\n 7,\n 1,\n ],\n [\n false,\n 'Yata',\n 'Congo (Kinshasa)',\n 'Road-250',\n '12/06/2020',\n '74-4291983',\n true,\n '47',\n 4,\n 4,\n ],\n [\n false,\n 'Brainlounge',\n 'Vietnam',\n 'Road-350-W',\n '10/03/2020',\n '83-0980643',\n true,\n '104',\n 2,\n 3,\n ],\n [\n false,\n 'Babblestorm',\n 'United States',\n 'Road-450',\n '10/10/2020',\n '19-2878430',\n true,\n '101',\n 6,\n 4,\n ],\n [\n false,\n 'Youspan',\n 'Brazil',\n 'Road-550-W',\n '16/12/2020',\n '19-1838230',\n true,\n '150',\n 10,\n 3,\n ],\n [\n false,\n 'Nlounge',\n 'China',\n 'Road-650',\n '31/10/2020',\n '32-2267938',\n true,\n '42',\n 4,\n 2,\n ],\n [\n false,\n 'Twinte',\n 'India',\n 'Road-750',\n '17/08/2020',\n '79-2821972',\n true,\n '144',\n 9,\n 3,\n ],\n [\n false,\n 'Oyonder',\n 'United Kingdom',\n 'Short-Sleeve Classic Jersey',\n '04/12/2020',\n '46-6597557',\n true,\n '195',\n 4,\n 1,\n ],\n [\n false,\n 'Gigabox',\n 'Pakistan',\n 'Sport-100',\n '03/02/2020',\n '15-1793960',\n true,\n '199',\n 4,\n 4,\n ],\n [\n false,\n 'Livetube',\n 'France',\n 'Touring-1000',\n '16/05/2020',\n '86-0811003',\n true,\n '110',\n 4,\n 5,\n ],\n [\n false,\n 'Voomm',\n 'United Kingdom',\n 'Touring-2000',\n '15/07/2020',\n '95-3068680',\n true,\n '51',\n 4,\n 4,\n ],\n [\n false,\n 'Voonyx',\n 'China',\n 'Touring-3000',\n '27/11/2020',\n '35-3085360',\n false,\n '69',\n 2,\n 5,\n ],\n [\n false,\n 'Zoombeat',\n 'United States',\n \"Women's Mountain Shorts\",\n '03/11/2020',\n '56-8673088',\n false,\n '53',\n 2,\n 3,\n ],\n [\n false,\n 'Roomm',\n 'China',\n \"Women's Tights\",\n '16/03/2020',\n '76-0085918',\n true,\n '168',\n 1,\n 1,\n ],\n [\n false,\n 'Leenti',\n 'China',\n 'Mountain-400',\n '16/05/2020',\n '03-0893276',\n false,\n '58',\n 1,\n 4,\n ],\n [\n false,\n 'Jetpulse',\n 'United States',\n 'Road-550',\n '08/02/2020',\n '79-9013306',\n true,\n '152',\n 9,\n 3,\n ],\n [\n false,\n 'Katz',\n 'Peru',\n 'Road-350',\n '15/02/2020',\n '55-7799920',\n true,\n '66',\n 4,\n 2,\n ],\n [\n false,\n 'Cogidoo',\n 'India',\n 'LL Mountain Front Wheel',\n '04/06/2020',\n '07-0881122',\n false,\n '112',\n 9,\n 2,\n ],\n [\n false,\n 'Divavu',\n 'Colombia',\n 'Touring Rear Wheel',\n '24/02/2020',\n '58-6157387',\n true,\n '50',\n 10,\n 4,\n ],\n [\n false,\n 'Mydeo',\n 'China',\n 'Touring Front Wheel',\n '07/12/2020',\n '12-2810010',\n false,\n '31',\n 3,\n 5,\n ],\n [\n false,\n 'Browsebug',\n 'Japan',\n 'ML Mountain Front Wheel',\n '14/01/2020',\n '64-9249984',\n true,\n '132',\n 5,\n 5,\n ],\n [\n false,\n 'Layo',\n 'China',\n 'HL Mountain Front Wheel',\n '24/04/2020',\n '45-0739652',\n true,\n '45',\n 1,\n 5,\n ],\n [\n false,\n 'Snaptags',\n 'United Kingdom',\n 'LL Touring Handlebars',\n '06/08/2020',\n '09-5712761',\n true,\n '197',\n 4,\n 2,\n ],\n [\n false,\n 'Cogilith',\n 'China',\n 'HL Touring Handlebars',\n '31/05/2020',\n '01-7345008',\n true,\n '190',\n 4,\n 3,\n ],\n [\n false,\n 'Reallinks',\n 'United Kingdom',\n 'LL Road Front Wheel',\n '14/05/2020',\n '62-1065350',\n true,\n '184',\n 3,\n 4,\n ],\n [\n false,\n 'Quaxo',\n 'United States',\n 'ML Road Front Wheel',\n '23/03/2020',\n '44-7241323',\n true,\n '169',\n 3,\n 4,\n ],\n [\n false,\n 'Devify',\n 'China',\n 'HL Road Front Wheel',\n '12/12/2020',\n '52-0295699',\n false,\n '152',\n 4,\n 4,\n ],\n [\n false,\n 'Youopia',\n 'Angola',\n 'LL Mountain Handlebars',\n '01/04/2020',\n '52-2650922',\n false,\n '182',\n 6,\n 4,\n ],\n [\n false,\n 'Ainyx',\n 'China',\n 'Touring Pedal',\n '27/02/2020',\n '48-3618525',\n true,\n '141',\n 6,\n 1,\n ],\n [\n false,\n 'Browsetype',\n 'Malaysia',\n 'ML Mountain Handlebars',\n '28/04/2020',\n '51-8893923',\n true,\n '169',\n 7,\n 1,\n ],\n [\n false,\n 'Muxo',\n 'China',\n 'HL Mountain Handlebars',\n '22/08/2020',\n '68-5911361',\n false,\n '39',\n 7,\n 1,\n ],\n [\n false,\n 'Bubbletube',\n 'China',\n 'LL Road Handlebars',\n '04/10/2020',\n '41-5880042',\n true,\n '71',\n 8,\n 3,\n ],\n [\n false,\n 'Fadeo',\n 'Vietnam',\n 'ML Road Handlebars',\n '23/04/2020',\n '90-5913983',\n true,\n '148',\n 10,\n 3,\n ],\n [\n false,\n 'Yadel',\n 'United Kingdom',\n 'HL Road Handlebars',\n '18/04/2020',\n '92-0960699',\n true,\n '116',\n 8,\n 1,\n ],\n [\n false,\n 'Blognation',\n 'China',\n 'LL Headset',\n '10/01/2020',\n '06-9493898',\n true,\n '96',\n 10,\n 1,\n ],\n [\n false,\n 'Devpoint',\n 'China',\n 'ML Headset',\n '25/12/2020',\n '69-5878565',\n true,\n '35',\n 4,\n 2,\n ],\n [\n false,\n 'Aibox',\n 'United Kingdom',\n 'HL Headset',\n '18/03/2020',\n '13-1133017',\n true,\n '16',\n 8,\n 2,\n ],\n [\n false,\n 'Brightdog',\n 'China',\n 'LL Mountain Pedal',\n '11/09/2020',\n '39-6530433',\n true,\n '194',\n 2,\n 5,\n ],\n [\n false,\n 'Gabcube',\n 'Nigeria',\n 'ML Mountain Pedal',\n '22/04/2020',\n '96-6860388',\n true,\n '24',\n 1,\n 3,\n ],\n [\n false,\n 'Muxo',\n 'China',\n 'HL Mountain Pedal',\n '05/06/2020',\n '30-0356137',\n true,\n '170',\n 4,\n 4,\n ],\n [\n false,\n 'Tambee',\n 'China',\n 'ML Touring Seat/Saddle',\n '22/02/2020',\n '93-9058255',\n true,\n '184',\n 9,\n 5,\n ],\n [\n false,\n 'Cogilith',\n 'India',\n 'LL Touring Seat/Saddle',\n '06/04/2020',\n '82-9268909',\n false,\n '153',\n 10,\n 4,\n ],\n [\n false,\n 'Dynabox',\n 'Hong Kong',\n 'HL Touring Seat/Saddle',\n '10/01/2020',\n '20-6913815',\n false,\n '88',\n 10,\n 1,\n ],\n [\n false,\n 'Shuffledrive',\n 'Sudan',\n 'LL Road Pedal',\n '16/09/2020',\n '08-8238817',\n true,\n '57',\n 9,\n 2,\n ],\n [\n false,\n 'Fivechat',\n 'China',\n 'ML Road Pedal',\n '26/08/2020',\n '44-7370350',\n false,\n '62',\n 4,\n 1,\n ],\n [\n false,\n 'Meembee',\n 'United States',\n 'HL Road Pedal',\n '27/12/2020',\n '01-3525949',\n true,\n '123',\n 2,\n 4,\n ],\n [\n false,\n 'Dynazzy',\n 'United Kingdom',\n 'LL Mountain Seat/Saddle 1',\n '15/12/2020',\n '04-2414623',\n true,\n '77',\n 10,\n 5,\n ],\n [\n false,\n 'Eare',\n 'China',\n 'ML Mountain Seat/Saddle 1',\n '04/04/2020',\n '15-1917509',\n false,\n '199',\n 9,\n 4,\n ],\n [\n false,\n 'Yozio',\n 'China',\n 'HL Mountain Seat/Saddle 1',\n '15/03/2020',\n '06-2526845',\n true,\n '149',\n 8,\n 2,\n ],\n [\n false,\n 'Quinu',\n \"Xi'an\",\n '425-777-7829',\n '22/02/2020',\n '83-1713558',\n false,\n '191',\n 9,\n 5,\n ],\n [\n false,\n 'Jazzy',\n 'United Kingdom',\n 'ML Road Seat/Saddle 1',\n '07/08/2020',\n '00-8892524',\n true,\n '150',\n 10,\n 2,\n ],\n [\n false,\n 'Thoughtsphere',\n 'China',\n 'HL Road Seat/Saddle 1',\n '28/11/2020',\n '39-5538991',\n true,\n '130',\n 7,\n 3,\n ],\n [\n false,\n 'Leenti',\n 'China',\n 'ML Road Rear Wheel',\n '29/12/2020',\n '06-9002973',\n true,\n '179',\n 1,\n 2,\n ],\n [\n false,\n 'Quaxo',\n 'United Kingdom',\n 'HL Road Rear Wheel',\n '06/09/2020',\n '73-6104901',\n true,\n '98',\n 5,\n 3,\n ],\n [\n false,\n 'Tanoodle',\n 'Chile',\n 'LL Mountain Seat/Saddle 2',\n '24/05/2020',\n '68-7384479',\n true,\n '175',\n 2,\n 3,\n ],\n [\n false,\n 'Feednation',\n 'China',\n 'ML Mountain Seat/Saddle 2',\n '21/11/2020',\n '26-7757763',\n true,\n '11',\n 1,\n 3,\n ],\n [\n false,\n 'Kayveo',\n 'China',\n 'HL Mountain Seat/Saddle 2',\n '21/06/2020',\n '07-4873562',\n false,\n '184',\n 7,\n 4,\n ],\n [\n false,\n 'Meevee',\n 'Saudi Arabia',\n 'LL Road Seat/Saddle 1',\n '16/11/2020',\n '46-5819554',\n false,\n '27',\n 9,\n 3,\n ],\n [\n false,\n 'Twitterworks',\n 'China',\n 'ML Road Seat/Saddle 2',\n '19/04/2020',\n '01-2666826',\n true,\n '186',\n 3,\n 2,\n ],\n [\n false,\n 'Wikizz',\n 'Tanzania',\n 'HL Road Seat/Saddle 2',\n '08/03/2020',\n '54-7090503',\n true,\n '20',\n 3,\n 3,\n ],\n [\n false,\n 'Yoveo',\n 'United States',\n 'LL Mountain Tire',\n '14/10/2020',\n '78-7658520',\n false,\n '153',\n 2,\n 1,\n ],\n [\n false,\n 'Yakidoo',\n 'China',\n 'ML Mountain Tire',\n '12/10/2020',\n '23-9926318',\n true,\n '161',\n 8,\n 5,\n ],\n [\n false,\n 'Oyope',\n 'China',\n 'HL Mountain Tire',\n '20/09/2020',\n '20-0179517',\n true,\n '98',\n 10,\n 5,\n ],\n [\n false,\n 'Skipstorm',\n 'United States',\n 'LL Road Tire',\n '01/10/2020',\n '02-9543343',\n true,\n '30',\n 7,\n 3,\n ],\n [\n false,\n 'Minyx',\n 'United States',\n 'ML Road Tire',\n '07/07/2020',\n '98-3938169',\n true,\n '73',\n 10,\n 2,\n ],\n [\n false,\n 'Miboo',\n 'China',\n 'HL Road Tire',\n '25/07/2020',\n '68-5197934',\n true,\n '158',\n 9,\n 1,\n ],\n [\n false,\n 'Realfire',\n 'United States',\n 'Touring Tire',\n '27/08/2020',\n '39-8260460',\n true,\n '122',\n 5,\n 2,\n ],\n [\n false,\n 'Shufflester',\n 'China',\n 'Mountain Tire Tube',\n '08/06/2020',\n '45-9776170',\n true,\n '33',\n 2,\n 4,\n ],\n [\n false,\n 'Ntag',\n 'China',\n 'Road Tire Tube',\n '06/12/2020',\n '45-0858451',\n true,\n '107',\n 6,\n 2,\n ],\n [\n false,\n 'Jabberbean',\n 'United States',\n 'Touring Tire Tube',\n '26/04/2020',\n '15-4247305',\n true,\n '15',\n 1,\n 2,\n ],\n [\n false,\n 'Thoughtblab',\n 'China',\n 'LL Bottom Bracket',\n '21/05/2020',\n '15-8534931',\n true,\n '168',\n 5,\n 2,\n ],\n [\n false,\n 'Jabbertype',\n 'China',\n 'Classic Vest',\n '25/07/2020',\n '23-1251557',\n true,\n '135',\n 4,\n 2,\n ],\n [\n false,\n 'Buzzshare',\n 'United Kingdom',\n 'Cycling Cap',\n '07/07/2020',\n '86-5920601',\n true,\n '11',\n 1,\n 4,\n ],\n [\n false,\n 'Roodel',\n 'United States',\n 'Full-Finger Gloves',\n '13/01/2020',\n '48-1055459',\n true,\n '41',\n 6,\n 4,\n ],\n [\n false,\n 'Zoovu',\n 'China',\n 'Half-Finger Gloves',\n '03/06/2020',\n '12-7842022',\n true,\n '144',\n 6,\n 1,\n ],\n [\n false,\n 'Photofeed',\n 'China',\n 'HL Mountain Frame',\n '14/07/2020',\n '94-5088099',\n true,\n '106',\n 1,\n 4,\n ],\n];\n\nexport const SELECTED_CLASS = 'selected';\nexport const ODD_ROW_CLASS = 'odd';\n\nexport function getData() {\n return data;\n}\n", + "/src/app/utils/hooks-callbacks.ts": "import Handsontable from 'handsontable';\nimport { ODD_ROW_CLASS } from './constants';\n\nconst headerAlignments = new Map([\n ['9', 'htCenter'],\n ['10', 'htRight'],\n ['12', 'htCenter'],\n]);\n\ntype AddClassesToRows = (\n TD: HTMLTableCellElement,\n row: number,\n column: number,\n prop: number | string,\n value: any,\n cellProperties: Handsontable.CellProperties\n) => void;\n\nexport const addClassesToRows: AddClassesToRows = (\n TD,\n row,\n column,\n prop,\n value,\n cellProperties\n) => {\n // Adding classes to `TR` just while rendering first visible `TD` element\n if (column !== 0) {\n return;\n }\n\n const parentElement = TD.parentElement;\n\n if (parentElement === null) {\n return;\n }\n\n // Add class to odd TRs\n if (row % 2 === 0) {\n Handsontable.dom.addClass(parentElement, ODD_ROW_CLASS);\n } else {\n Handsontable.dom.removeClass(parentElement, ODD_ROW_CLASS);\n }\n};\n\nexport function alignHeaders(\n this: Handsontable,\n column: number,\n TH: HTMLTableCellElement\n) {\n if (column < 0) {\n return;\n }\n\n const alignmentClass = this.isRtl() ? 'htRight' : 'htLeft';\n\n if (TH.firstChild) {\n if (headerAlignments.has(column.toString())) {\n Handsontable.dom.removeClass(\n TH.firstChild as HTMLElement,\n alignmentClass\n );\n Handsontable.dom.addClass(\n TH.firstChild as HTMLElement,\n headerAlignments.get(column.toString()) as string\n );\n } else {\n Handsontable.dom.addClass(TH.firstChild as HTMLElement, alignmentClass);\n }\n }\n}\n", + "/src/index.html": "\n\n \n \n Sandbox\n \n \n \n \n \n \n \n \n\n", + "/src/main.ts": "import { bootstrapApplication } from '@angular/platform-browser';\nimport { appConfig } from './app/app.config';\nimport { DataGridComponent } from './app/data-grid.component';\n\nbootstrapApplication(DataGridComponent, appConfig).catch((err) =>\n console.error(err)\n);\n", + "/src/styles.css": "/* You can add global styles to this file, and also import other style files */\n", + "/tsconfig.app.json": "/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */\n/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */\n{\n \"extends\": \"./tsconfig.json\",\n \"compilerOptions\": {\n \"outDir\": \"./out-tsc/app\",\n \"types\": []\n },\n \"files\": [\n \"src/main.ts\"\n ],\n \"include\": [\n \"src/**/*.d.ts\"\n ]\n}\n", + "/tsconfig.json": "/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */\n/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */\n{\n \"compileOnSave\": false,\n \"compilerOptions\": {\n \"baseUrl\": \"./\",\n \"outDir\": \"./dist/out-tsc\",\n \"strict\": true,\n \"noImplicitOverride\": true,\n \"noPropertyAccessFromIndexSignature\": true,\n \"noImplicitReturns\": true,\n \"noFallthroughCasesInSwitch\": true,\n \"skipLibCheck\": true,\n \"esModuleInterop\": true,\n \"sourceMap\": true,\n \"declaration\": false,\n \"experimentalDecorators\": true,\n \"moduleResolution\": \"bundler\",\n \"importHelpers\": true,\n \"target\": \"ES2022\",\n \"module\": \"ES2022\",\n \"lib\": [\n \"ES2022\",\n \"dom\"\n ]\n },\n \"angularCompilerOptions\": {\n \"enableI18nLegacyMessageIdFormat\": false,\n \"strictInjectionParameters\": true,\n \"strictInputAccessModifiers\": true,\n \"strictTemplates\": true\n }\n}\n", + "/tsconfig.spec.json": "/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */\n/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */\n{\n \"extends\": \"./tsconfig.json\",\n \"compilerOptions\": {\n \"outDir\": \"./out-tsc/spec\",\n \"types\": [\n \"jasmine\"\n ]\n },\n \"include\": [\n \"src/**/*.spec.ts\",\n \"src/**/*.d.ts\"\n ]\n}\n" + } + }, + { + "framework": "next.js", + "displayName": "Next.js (App Router)", + "tier": 2, + "sandpackTemplate": null, + "container": "next", + "htWrappers": [ + "@handsontable/react-wrapper" + ], + "entry": "/app/page.tsx", + "htmlEntry": null, + "devCommand": "next dev -p 3000 -H 0.0.0.0", + "buildCommand": "next build", + "outputDir": "out", + "outputGlob": null, + "staticExport": true, + "spaMode": false, + "port": 3000, + "installCommand": "npm install", + "htCoreRange": "17.0.1", + "fileCount": 16, + "assets": [ + "app/favicon.ico" + ], + "skipped": [], + "files": { + "/.eslintrc.json": "{\n \"extends\": \"next/core-web-vitals\"\n}\n", + "/.gitignore": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/.pnp\n.pnp.js\n\n# testing\n/coverage\n\n# next.js\n/.next/\n/out/\n\n# production\n/build\n\n# misc\n.DS_Store\n*.pem\n\n# debug\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n\n# local env files\n.env*.local\n\n# vercel\n.vercel\n\n# typescript\n*.tsbuildinfo\nnext-env.d.ts\n", + "/README.md": "# Demo\n\n## Description\n\nThis example demonstrates how to set up Handsontable with React and Next.js, providing a foundation for building interactive data-driven applications.\n\n## Installation\n\nInstall all necessary dependencies from npm with `npm install`.\n\n## Development\n\nFirst, run the development server:\n\n```bash\nnpm run dev\n# or\nyarn dev\n# or\npnpm dev\n# or\nbun run dev\n```\n\nYou can then visit http://localhost:3000 with your browser to view the project. You can start editing the page by modifying app/page.tsx. The page auto-updates as you edit the file.\n\n## License\n\nHandsontable is a commercial software with two licenses available:\n\n- Free for non-commercial purposes such as teaching, academic research, and evaluation. [Read it here](https://github.com/handsontable/handsontable/blob/master/handsontable-non-commercial-license.pdf).\n- Commercial license with support and maintenance included. See [pricing plans](https://handsontable.com/pricing).\n\n## Get help\n\nJoin our [community](https://github.com/handsontable/handsontable/discussions) to get help, share ideas, and contribute to the ongoing development of Handsontable!\n\nCommercial license holders with an active support plan can contact our [Support Team](https://handsontable.com/contact?category=technical_support).\n", + "/app/data.ts": "export type Data = typeof data;\n\nexport const data = [\n [\n false,\n 'Tagcat',\n 'United Kingdom',\n 'Classic Vest',\n '11/10/2020',\n '01-2331942',\n true,\n '172',\n 2,\n 2,\n ],\n [\n true,\n 'Zoomzone',\n 'Indonesia',\n 'Cycling Cap',\n '03/05/2020',\n '88-2768633',\n true,\n '188',\n 6,\n 2,\n ],\n [\n true,\n 'Meeveo',\n 'United States',\n 'Full-Finger Gloves',\n '27/03/2020',\n '51-6775945',\n true,\n '162',\n 1,\n 3,\n ],\n [\n false,\n 'Buzzdog',\n 'Philippines',\n 'HL Mountain Frame',\n '29/08/2020',\n '44-4028109',\n true,\n '133',\n 7,\n 1,\n ],\n [\n true,\n 'Katz',\n 'India',\n 'Half-Finger Gloves',\n '02/10/2020',\n '08-2758492',\n true,\n '87',\n 1,\n 3,\n ],\n [\n false,\n 'Jaxbean',\n 'China',\n 'HL Road Frame',\n '28/09/2020',\n '84-3557705',\n false,\n '26',\n 8,\n 1,\n ],\n [\n false,\n 'Wikido',\n 'Brazil',\n 'HL Touring Frame',\n '24/06/2020',\n '20-9397637',\n false,\n '110',\n 4,\n 1,\n ],\n [\n false,\n 'Browsedrive',\n 'United States',\n 'LL Mountain Frame',\n '13/03/2020',\n '36-0079556',\n true,\n '50',\n 4,\n 4,\n ],\n [\n false,\n 'Twinder',\n 'United Kingdom',\n 'LL Road Frame',\n '06/04/2020',\n '41-1489542',\n false,\n '160',\n 6,\n 1,\n ],\n [\n false,\n 'Jetwire',\n 'China',\n 'LL Touring Frame',\n '01/02/2020',\n '37-1531629',\n true,\n '30',\n 8,\n 5,\n ],\n [\n false,\n 'Chatterpoint',\n 'China',\n 'Long-Sleeve Logo Jersey',\n '14/07/2020',\n '25-5083429',\n true,\n '39',\n 7,\n 2,\n ],\n [\n false,\n 'Twinder',\n 'Egypt',\n \"Men's Bib-Shorts\",\n '31/08/2020',\n '04-4281278',\n false,\n '96',\n 6,\n 1,\n ],\n [\n false,\n 'Midel',\n 'United States',\n \"Men's Sports Shorts\",\n '27/06/2020',\n '55-1711908',\n true,\n '108',\n 10,\n 3,\n ],\n [\n false,\n 'Yodo',\n 'India',\n 'ML Mountain Frame',\n '16/03/2020',\n '58-8360815',\n false,\n '46',\n 1,\n 1,\n ],\n [\n false,\n 'Camido',\n 'Russia',\n 'ML Mountain Frame-W',\n '13/09/2020',\n '10-3786104',\n true,\n '97',\n 8,\n 3,\n ],\n [\n false,\n 'Eire',\n 'Thailand',\n 'ML Road Frame',\n '10/04/2020',\n '45-1186054',\n true,\n '161',\n 1,\n 4,\n ],\n [\n false,\n 'Vinte',\n 'United Kingdom',\n 'ML Road Frame-W',\n '22/01/2020',\n '62-6202742',\n true,\n '58',\n 4,\n 3,\n ],\n [\n false,\n 'Twitterlist',\n 'China',\n 'Mountain Bike Socks',\n '09/11/2020',\n '88-9646223',\n true,\n '92',\n 8,\n 3,\n ],\n [\n false,\n 'Eidel',\n 'Bangladesh',\n 'Mountain-100',\n '19/09/2020',\n '45-5588112',\n true,\n '5',\n 6,\n 5,\n ],\n [\n false,\n 'Trunyx',\n 'Nigeria',\n 'Mountain-200',\n '09/03/2020',\n '66-6271819',\n true,\n '158',\n 4,\n 1,\n ],\n [\n false,\n 'Katz',\n 'Turkey',\n 'Mountain-300',\n '05/03/2020',\n '38-9245023',\n false,\n '121',\n 5,\n 4,\n ],\n [\n false,\n 'Kaymbo',\n 'United States',\n 'Mountain-400-W',\n '24/12/2020',\n '44-5916927',\n false,\n '61',\n 5,\n 4,\n ],\n [\n false,\n 'Ozu',\n 'Pakistan',\n 'Mountain-500',\n '13/06/2020',\n '31-5449914',\n true,\n '155',\n 2,\n 2,\n ],\n [\n false,\n 'Rhynyx',\n 'India',\n 'Racing Socks',\n '05/12/2020',\n '19-9413869',\n true,\n '162',\n 2,\n 4,\n ],\n [\n false,\n 'Flashset',\n 'Iran',\n 'Road-150',\n '14/12/2020',\n '25-9807605',\n false,\n '46',\n 7,\n 1,\n ],\n [\n false,\n 'Yata',\n 'Congo (Kinshasa)',\n 'Road-250',\n '12/06/2020',\n '74-4291983',\n true,\n '47',\n 4,\n 4,\n ],\n [\n false,\n 'Brainlounge',\n 'Vietnam',\n 'Road-350-W',\n '10/03/2020',\n '83-0980643',\n true,\n '104',\n 2,\n 3,\n ],\n [\n false,\n 'Babblestorm',\n 'United States',\n 'Road-450',\n '10/10/2020',\n '19-2878430',\n true,\n '101',\n 6,\n 4,\n ],\n [\n false,\n 'Youspan',\n 'Brazil',\n 'Road-550-W',\n '16/12/2020',\n '19-1838230',\n true,\n '150',\n 10,\n 3,\n ],\n [\n false,\n 'Nlounge',\n 'China',\n 'Road-650',\n '31/10/2020',\n '32-2267938',\n true,\n '42',\n 4,\n 2,\n ],\n [\n false,\n 'Twinte',\n 'India',\n 'Road-750',\n '17/08/2020',\n '79-2821972',\n true,\n '144',\n 9,\n 3,\n ],\n [\n false,\n 'Oyonder',\n 'United Kingdom',\n 'Short-Sleeve Classic Jersey',\n '04/12/2020',\n '46-6597557',\n true,\n '195',\n 4,\n 1,\n ],\n [\n false,\n 'Gigabox',\n 'Pakistan',\n 'Sport-100',\n '03/02/2020',\n '15-1793960',\n true,\n '199',\n 4,\n 4,\n ],\n [\n false,\n 'Livetube',\n 'France',\n 'Touring-1000',\n '16/05/2020',\n '86-0811003',\n true,\n '110',\n 4,\n 5,\n ],\n [\n false,\n 'Voomm',\n 'United Kingdom',\n 'Touring-2000',\n '15/07/2020',\n '95-3068680',\n true,\n '51',\n 4,\n 4,\n ],\n [\n false,\n 'Voonyx',\n 'China',\n 'Touring-3000',\n '27/11/2020',\n '35-3085360',\n false,\n '69',\n 2,\n 5,\n ],\n [\n false,\n 'Zoombeat',\n 'United States',\n \"Women's Mountain Shorts\",\n '03/11/2020',\n '56-8673088',\n false,\n '53',\n 2,\n 3,\n ],\n [\n false,\n 'Roomm',\n 'China',\n \"Women's Tights\",\n '16/03/2020',\n '76-0085918',\n true,\n '168',\n 1,\n 1,\n ],\n [\n false,\n 'Leenti',\n 'China',\n 'Mountain-400',\n '16/05/2020',\n '03-0893276',\n false,\n '58',\n 1,\n 4,\n ],\n [\n false,\n 'Jetpulse',\n 'United States',\n 'Road-550',\n '08/02/2020',\n '79-9013306',\n true,\n '152',\n 9,\n 3,\n ],\n [\n false,\n 'Katz',\n 'Peru',\n 'Road-350',\n '15/02/2020',\n '55-7799920',\n true,\n '66',\n 4,\n 2,\n ],\n [\n false,\n 'Cogidoo',\n 'India',\n 'LL Mountain Front Wheel',\n '04/06/2020',\n '07-0881122',\n false,\n '112',\n 9,\n 2,\n ],\n [\n false,\n 'Divavu',\n 'Colombia',\n 'Touring Rear Wheel',\n '24/02/2020',\n '58-6157387',\n true,\n '50',\n 10,\n 4,\n ],\n [\n false,\n 'Mydeo',\n 'China',\n 'Touring Front Wheel',\n '07/12/2020',\n '12-2810010',\n false,\n '31',\n 3,\n 5,\n ],\n [\n false,\n 'Browsebug',\n 'Japan',\n 'ML Mountain Front Wheel',\n '14/01/2020',\n '64-9249984',\n true,\n '132',\n 5,\n 5,\n ],\n [\n false,\n 'Layo',\n 'China',\n 'HL Mountain Front Wheel',\n '24/04/2020',\n '45-0739652',\n true,\n '45',\n 1,\n 5,\n ],\n [\n false,\n 'Snaptags',\n 'United Kingdom',\n 'LL Touring Handlebars',\n '06/08/2020',\n '09-5712761',\n true,\n '197',\n 4,\n 2,\n ],\n [\n false,\n 'Cogilith',\n 'China',\n 'HL Touring Handlebars',\n '31/05/2020',\n '01-7345008',\n true,\n '190',\n 4,\n 3,\n ],\n [\n false,\n 'Reallinks',\n 'United Kingdom',\n 'LL Road Front Wheel',\n '14/05/2020',\n '62-1065350',\n true,\n '184',\n 3,\n 4,\n ],\n [\n false,\n 'Quaxo',\n 'United States',\n 'ML Road Front Wheel',\n '23/03/2020',\n '44-7241323',\n true,\n '169',\n 3,\n 4,\n ],\n [\n false,\n 'Devify',\n 'China',\n 'HL Road Front Wheel',\n '12/12/2020',\n '52-0295699',\n false,\n '152',\n 4,\n 4,\n ],\n [\n false,\n 'Youopia',\n 'Angola',\n 'LL Mountain Handlebars',\n '01/04/2020',\n '52-2650922',\n false,\n '182',\n 6,\n 4,\n ],\n [\n false,\n 'Ainyx',\n 'China',\n 'Touring Pedal',\n '27/02/2020',\n '48-3618525',\n true,\n '141',\n 6,\n 1,\n ],\n [\n false,\n 'Browsetype',\n 'Malaysia',\n 'ML Mountain Handlebars',\n '28/04/2020',\n '51-8893923',\n true,\n '169',\n 7,\n 1,\n ],\n [\n false,\n 'Muxo',\n 'China',\n 'HL Mountain Handlebars',\n '22/08/2020',\n '68-5911361',\n false,\n '39',\n 7,\n 1,\n ],\n [\n false,\n 'Bubbletube',\n 'China',\n 'LL Road Handlebars',\n '04/10/2020',\n '41-5880042',\n true,\n '71',\n 8,\n 3,\n ],\n [\n false,\n 'Fadeo',\n 'Vietnam',\n 'ML Road Handlebars',\n '23/04/2020',\n '90-5913983',\n true,\n '148',\n 10,\n 3,\n ],\n [\n false,\n 'Yadel',\n 'United Kingdom',\n 'HL Road Handlebars',\n '18/04/2020',\n '92-0960699',\n true,\n '116',\n 8,\n 1,\n ],\n [\n false,\n 'Blognation',\n 'China',\n 'LL Headset',\n '10/01/2020',\n '06-9493898',\n true,\n '96',\n 10,\n 1,\n ],\n [\n false,\n 'Devpoint',\n 'China',\n 'ML Headset',\n '25/12/2020',\n '69-5878565',\n true,\n '35',\n 4,\n 2,\n ],\n [\n false,\n 'Aibox',\n 'United Kingdom',\n 'HL Headset',\n '18/03/2020',\n '13-1133017',\n true,\n '16',\n 8,\n 2,\n ],\n [\n false,\n 'Brightdog',\n 'China',\n 'LL Mountain Pedal',\n '11/09/2020',\n '39-6530433',\n true,\n '194',\n 2,\n 5,\n ],\n [\n false,\n 'Gabcube',\n 'Nigeria',\n 'ML Mountain Pedal',\n '22/04/2020',\n '96-6860388',\n true,\n '24',\n 1,\n 3,\n ],\n [\n false,\n 'Muxo',\n 'China',\n 'HL Mountain Pedal',\n '05/06/2020',\n '30-0356137',\n true,\n '170',\n 4,\n 4,\n ],\n [\n false,\n 'Tambee',\n 'China',\n 'ML Touring Seat/Saddle',\n '22/02/2020',\n '93-9058255',\n true,\n '184',\n 9,\n 5,\n ],\n [\n false,\n 'Cogilith',\n 'India',\n 'LL Touring Seat/Saddle',\n '06/04/2020',\n '82-9268909',\n false,\n '153',\n 10,\n 4,\n ],\n [\n false,\n 'Dynabox',\n 'Hong Kong',\n 'HL Touring Seat/Saddle',\n '10/01/2020',\n '20-6913815',\n false,\n '88',\n 10,\n 1,\n ],\n [\n false,\n 'Shuffledrive',\n 'Sudan',\n 'LL Road Pedal',\n '16/09/2020',\n '08-8238817',\n true,\n '57',\n 9,\n 2,\n ],\n [\n false,\n 'Fivechat',\n 'China',\n 'ML Road Pedal',\n '26/08/2020',\n '44-7370350',\n false,\n '62',\n 4,\n 1,\n ],\n [\n false,\n 'Meembee',\n 'United States',\n 'HL Road Pedal',\n '27/12/2020',\n '01-3525949',\n true,\n '123',\n 2,\n 4,\n ],\n [\n false,\n 'Dynazzy',\n 'United Kingdom',\n 'LL Mountain Seat/Saddle 1',\n '15/12/2020',\n '04-2414623',\n true,\n '77',\n 10,\n 5,\n ],\n [\n false,\n 'Eare',\n 'China',\n 'ML Mountain Seat/Saddle 1',\n '04/04/2020',\n '15-1917509',\n false,\n '199',\n 9,\n 4,\n ],\n [\n false,\n 'Yozio',\n 'China',\n 'HL Mountain Seat/Saddle 1',\n '15/03/2020',\n '06-2526845',\n true,\n '149',\n 8,\n 2,\n ],\n [\n false,\n 'Quinu',\n \"Xi'an\",\n '425-777-7829',\n '22/02/2020',\n '83-1713558',\n false,\n '191',\n 9,\n 5,\n ],\n [\n false,\n 'Jazzy',\n 'United Kingdom',\n 'ML Road Seat/Saddle 1',\n '07/08/2020',\n '00-8892524',\n true,\n '150',\n 10,\n 2,\n ],\n [\n false,\n 'Thoughtsphere',\n 'China',\n 'HL Road Seat/Saddle 1',\n '28/11/2020',\n '39-5538991',\n true,\n '130',\n 7,\n 3,\n ],\n [\n false,\n 'Leenti',\n 'China',\n 'ML Road Rear Wheel',\n '29/12/2020',\n '06-9002973',\n true,\n '179',\n 1,\n 2,\n ],\n [\n false,\n 'Quaxo',\n 'United Kingdom',\n 'HL Road Rear Wheel',\n '06/09/2020',\n '73-6104901',\n true,\n '98',\n 5,\n 3,\n ],\n [\n false,\n 'Tanoodle',\n 'Chile',\n 'LL Mountain Seat/Saddle 2',\n '24/05/2020',\n '68-7384479',\n true,\n '175',\n 2,\n 3,\n ],\n [\n false,\n 'Feednation',\n 'China',\n 'ML Mountain Seat/Saddle 2',\n '21/11/2020',\n '26-7757763',\n true,\n '11',\n 1,\n 3,\n ],\n [\n false,\n 'Kayveo',\n 'China',\n 'HL Mountain Seat/Saddle 2',\n '21/06/2020',\n '07-4873562',\n false,\n '184',\n 7,\n 4,\n ],\n [\n false,\n 'Meevee',\n 'Saudi Arabia',\n 'LL Road Seat/Saddle 1',\n '16/11/2020',\n '46-5819554',\n false,\n '27',\n 9,\n 3,\n ],\n [\n false,\n 'Twitterworks',\n 'China',\n 'ML Road Seat/Saddle 2',\n '19/04/2020',\n '01-2666826',\n true,\n '186',\n 3,\n 2,\n ],\n [\n false,\n 'Wikizz',\n 'Tanzania',\n 'HL Road Seat/Saddle 2',\n '08/03/2020',\n '54-7090503',\n true,\n '20',\n 3,\n 3,\n ],\n [\n false,\n 'Yoveo',\n 'United States',\n 'LL Mountain Tire',\n '14/10/2020',\n '78-7658520',\n false,\n '153',\n 2,\n 1,\n ],\n [\n false,\n 'Yakidoo',\n 'China',\n 'ML Mountain Tire',\n '12/10/2020',\n '23-9926318',\n true,\n '161',\n 8,\n 5,\n ],\n [\n false,\n 'Oyope',\n 'China',\n 'HL Mountain Tire',\n '20/09/2020',\n '20-0179517',\n true,\n '98',\n 10,\n 5,\n ],\n [\n false,\n 'Skipstorm',\n 'United States',\n 'LL Road Tire',\n '01/10/2020',\n '02-9543343',\n true,\n '30',\n 7,\n 3,\n ],\n [\n false,\n 'Minyx',\n 'United States',\n 'ML Road Tire',\n '07/07/2020',\n '98-3938169',\n true,\n '73',\n 10,\n 2,\n ],\n [\n false,\n 'Miboo',\n 'China',\n 'HL Road Tire',\n '25/07/2020',\n '68-5197934',\n true,\n '158',\n 9,\n 1,\n ],\n [\n false,\n 'Realfire',\n 'United States',\n 'Touring Tire',\n '27/08/2020',\n '39-8260460',\n true,\n '122',\n 5,\n 2,\n ],\n [\n false,\n 'Shufflester',\n 'China',\n 'Mountain Tire Tube',\n '08/06/2020',\n '45-9776170',\n true,\n '33',\n 2,\n 4,\n ],\n [\n false,\n 'Ntag',\n 'China',\n 'Road Tire Tube',\n '06/12/2020',\n '45-0858451',\n true,\n '107',\n 6,\n 2,\n ],\n [\n false,\n 'Jabberbean',\n 'United States',\n 'Touring Tire Tube',\n '26/04/2020',\n '15-4247305',\n true,\n '15',\n 1,\n 2,\n ],\n [\n false,\n 'Thoughtblab',\n 'China',\n 'LL Bottom Bracket',\n '21/05/2020',\n '15-8534931',\n true,\n '168',\n 5,\n 2,\n ],\n [\n false,\n 'Jabbertype',\n 'China',\n 'Classic Vest',\n '25/07/2020',\n '23-1251557',\n true,\n '135',\n 4,\n 2,\n ],\n [\n false,\n 'Buzzshare',\n 'United Kingdom',\n 'Cycling Cap',\n '07/07/2020',\n '86-5920601',\n true,\n '11',\n 1,\n 4,\n ],\n [\n false,\n 'Roodel',\n 'United States',\n 'Full-Finger Gloves',\n '13/01/2020',\n '48-1055459',\n true,\n '41',\n 6,\n 4,\n ],\n [\n false,\n 'Zoovu',\n 'China',\n 'Half-Finger Gloves',\n '03/06/2020',\n '12-7842022',\n true,\n '144',\n 6,\n 1,\n ],\n [\n false,\n 'Photofeed',\n 'China',\n 'HL Mountain Frame',\n '14/07/2020',\n '94-5088099',\n true,\n '106',\n 1,\n 4,\n ],\n];\n", + "/app/globals.css": "@tailwind base;\n@tailwind components;\n@tailwind utilities;\n", + "/app/layout.tsx": "import type { Metadata } from 'next';\nimport { Inter } from 'next/font/google';\nimport './globals.css';\n\nconst inter = Inter({ subsets: ['latin'] });\n\nexport const metadata: Metadata = {\n title: 'Handsontable Next.js Example',\n description: 'Handsontable Next.js Example',\n};\n\nexport default function RootLayout({\n children,\n}: {\n children: React.ReactNode;\n}) {\n return (\n \n {children}\n \n );\n}\n", + "/app/page.tsx": "import Grid from '@/components/Grid';\nimport { data } from './data';\n\nexport default function Home() {\n return (\n
\n \n
\n );\n}\n", + "/components/Grid.tsx": "'use client';\n\nimport {\n AutoColumnSize,\n Autofill,\n ContextMenu,\n CopyPaste,\n DropdownMenu,\n Filters,\n HiddenRows,\n registerPlugin,\n} from 'handsontable/plugins';\n\nimport {\n CheckboxCellType,\n NumericCellType,\n registerCellType,\n} from 'handsontable/cellTypes';\nimport { HotTable, HotColumn } from '@handsontable/react-wrapper';\n\nimport 'handsontable/styles/handsontable.min.css';\nimport 'handsontable/styles/ht-theme-main.min.css';\n\nimport { Data } from '@/app/data';\n\nregisterCellType(CheckboxCellType);\nregisterCellType(NumericCellType);\n\nregisterPlugin(AutoColumnSize);\nregisterPlugin(Autofill);\nregisterPlugin(ContextMenu);\nregisterPlugin(CopyPaste);\nregisterPlugin(DropdownMenu);\nregisterPlugin(Filters);\nregisterPlugin(HiddenRows);\n\ntype GridProps = {\n data: Data;\n};\n\nexport default function Grid(props: GridProps) {\n return (\n
\n \n \n \n \n \n \n \n \n \n \n \n
\n );\n}\n", + "/next-env.d.ts": "/// \n/// \n\n// NOTE: This file should not be edited\n// see https://nextjs.org/docs/basic-features/typescript for more information.\n", + "/next.config.js": "/** @type {import('next').NextConfig} */\nconst nextConfig = {};\n\nmodule.exports = nextConfig;\n", + "/package.json": "{\n \"name\": \"nextjs\",\n \"version\": \"0.1.0\",\n \"private\": true,\n \"scripts\": {\n \"dev\": \"next dev\",\n \"build\": \"next build\",\n \"start\": \"next start\",\n \"lint\": \"next lint\"\n },\n \"dependencies\": {\n \"@handsontable/react-wrapper\": \"17.0.1\",\n \"handsontable\": \"17.0.1\",\n \"@types/node\": \"20.6.2\",\n \"@types/react\": \"18.2.22\",\n \"@types/react-dom\": \"18.2.7\",\n \"autoprefixer\": \"10.4.15\",\n \"eslint\": \"8.49.0\",\n \"eslint-config-next\": \"13.5.1\",\n \"next\": \"13.5.1\",\n \"postcss\": \"8.4.30\",\n \"react\": \"18.2.0\",\n \"react-dom\": \"18.2.0\",\n \"tailwindcss\": \"3.3.3\",\n \"typescript\": \"5.2.2\"\n },\n \"config\": {\n \"codesandbox\": {\n \"templateId\": \"5l26fv\"\n },\n \"stackblitz\": {\n \"template\": \"node\"\n }\n }\n}\n", + "/postcss.config.js": "module.exports = {\n plugins: {\n tailwindcss: {},\n autoprefixer: {},\n },\n};\n", + "/public/next.svg": "", + "/public/vercel.svg": "", + "/tailwind.config.ts": "import type { Config } from 'tailwindcss';\n\nconst config: Config = {\n content: [\n './pages/**/*.{js,ts,jsx,tsx,mdx}',\n './components/**/*.{js,ts,jsx,tsx,mdx}',\n './app/**/*.{js,ts,jsx,tsx,mdx}',\n ],\n theme: {\n extend: {\n backgroundImage: {\n 'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))',\n 'gradient-conic':\n 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))',\n },\n },\n },\n plugins: [],\n};\nexport default config;\n", + "/tsconfig.json": "{\n \"compilerOptions\": {\n \"target\": \"es5\",\n \"lib\": [\"dom\", \"dom.iterable\", \"esnext\"],\n \"allowJs\": true,\n \"skipLibCheck\": true,\n \"strict\": true,\n \"noEmit\": true,\n \"esModuleInterop\": true,\n \"module\": \"esnext\",\n \"moduleResolution\": \"bundler\",\n \"resolveJsonModule\": true,\n \"isolatedModules\": true,\n \"jsx\": \"preserve\",\n \"incremental\": true,\n \"plugins\": [\n {\n \"name\": \"next\"\n }\n ],\n \"paths\": {\n \"@/*\": [\"./*\"]\n }\n },\n \"include\": [\"next-env.d.ts\", \"**/*.ts\", \"**/*.tsx\", \".next/types/**/*.ts\"],\n \"exclude\": [\"node_modules\"]\n}\n" + } + }, + { + "framework": "next-shadcn.js", + "displayName": "Next.js + shadcn/ui", + "tier": 2, + "sandpackTemplate": null, + "container": "next", + "htWrappers": [ + "@handsontable/react-wrapper" + ], + "entry": "/app/page.tsx", + "htmlEntry": null, + "devCommand": "next dev -p 3000 -H 0.0.0.0", + "buildCommand": "next build", + "outputDir": "out", + "outputGlob": null, + "staticExport": true, + "spaMode": false, + "port": 3000, + "installCommand": "npm install", + "htCoreRange": "17.0.1", + "fileCount": 19, + "assets": [ + "app/favicon.ico" + ], + "skipped": [], + "files": { + "/.gitignore": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/.pnp\n.pnp.*\n.yarn/*\n!.yarn/patches\n!.yarn/plugins\n!.yarn/releases\n!.yarn/versions\n\n# testing\n/coverage\n\n# next.js\n/.next/\n/out/\n\n# production\n/build\n\n# misc\n.DS_Store\n*.pem\n\n# debug\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n.pnpm-debug.log*\n\n# env files (can opt-in for committing if needed)\n.env*\n\n# vercel\n.vercel\n\n# typescript\n*.tsbuildinfo\nnext-env.d.ts\n", + "/README.md": "This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).\n\n## Getting Started\n\nFirst, run the development server:\n\n```bash\nnpm run dev\n# or\nyarn dev\n# or\npnpm dev\n# or\nbun dev\n```\n\nOpen [http://localhost:3000](http://localhost:3000) with your browser to see the result.\n\nYou can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.\n\nThis project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.\n\n## Learn More\n\nTo learn more about Next.js, take a look at the following resources:\n\n- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.\n- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.\n\nYou can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!\n\n## Deploy on Vercel\n\nThe easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.\n\nCheck out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.\n", + "/app/globals.css": "@import \"tailwindcss\";\n@import \"tw-animate-css\";\n@import \"shadcn/tailwind.css\";\n\n@custom-variant dark (&:is(.dark *));\n\n@theme inline {\n --color-background: var(--background);\n --color-foreground: var(--foreground);\n --font-sans: var(--font-geist-sans);\n --font-mono: var(--font-geist-mono);\n --color-sidebar-ring: var(--sidebar-ring);\n --color-sidebar-border: var(--sidebar-border);\n --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);\n --color-sidebar-accent: var(--sidebar-accent);\n --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);\n --color-sidebar-primary: var(--sidebar-primary);\n --color-sidebar-foreground: var(--sidebar-foreground);\n --color-sidebar: var(--sidebar);\n --color-chart-5: var(--chart-5);\n --color-chart-4: var(--chart-4);\n --color-chart-3: var(--chart-3);\n --color-chart-2: var(--chart-2);\n --color-chart-1: var(--chart-1);\n --color-ring: var(--ring);\n --color-input: var(--input);\n --color-border: var(--border);\n --color-destructive: var(--destructive);\n --color-accent-foreground: var(--accent-foreground);\n --color-accent: var(--accent);\n --color-muted-foreground: var(--muted-foreground);\n --color-muted: var(--muted);\n --color-secondary-foreground: var(--secondary-foreground);\n --color-secondary: var(--secondary);\n --color-primary-foreground: var(--primary-foreground);\n --color-primary: var(--primary);\n --color-popover-foreground: var(--popover-foreground);\n --color-popover: var(--popover);\n --color-card-foreground: var(--card-foreground);\n --color-card: var(--card);\n --radius-sm: calc(var(--radius) - 4px);\n --radius-md: calc(var(--radius) - 2px);\n --radius-lg: var(--radius);\n --radius-xl: calc(var(--radius) + 4px);\n --radius-2xl: calc(var(--radius) + 8px);\n --radius-3xl: calc(var(--radius) + 12px);\n --radius-4xl: calc(var(--radius) + 16px);\n}\n\n:root {\n --radius: 0.625rem;\n --background: oklch(1 0 0);\n --foreground: oklch(0.145 0 0);\n --card: oklch(1 0 0);\n --card-foreground: oklch(0.145 0 0);\n --popover: oklch(1 0 0);\n --popover-foreground: oklch(0.145 0 0);\n --primary: oklch(0.205 0 0);\n --primary-foreground: oklch(0.985 0 0);\n --secondary: oklch(0.97 0 0);\n --secondary-foreground: oklch(0.205 0 0);\n --muted: oklch(0.97 0 0);\n --muted-foreground: oklch(0.556 0 0);\n --accent: oklch(0.97 0 0);\n --accent-foreground: oklch(0.205 0 0);\n --destructive: oklch(0.577 0.245 27.325);\n --border: oklch(0.922 0 0);\n --input: oklch(0.922 0 0);\n --ring: oklch(0.708 0 0);\n --chart-1: oklch(0.646 0.222 41.116);\n --chart-2: oklch(0.6 0.118 184.704);\n --chart-3: oklch(0.398 0.07 227.392);\n --chart-4: oklch(0.828 0.189 84.429);\n --chart-5: oklch(0.769 0.188 70.08);\n --sidebar: oklch(0.985 0 0);\n --sidebar-foreground: oklch(0.145 0 0);\n --sidebar-primary: oklch(0.205 0 0);\n --sidebar-primary-foreground: oklch(0.985 0 0);\n --sidebar-accent: oklch(0.97 0 0);\n --sidebar-accent-foreground: oklch(0.205 0 0);\n --sidebar-border: oklch(0.922 0 0);\n --sidebar-ring: oklch(0.708 0 0);\n}\n\n.dark {\n --background: oklch(0.145 0 0);\n --foreground: oklch(0.985 0 0);\n --card: oklch(0.205 0 0);\n --card-foreground: oklch(0.985 0 0);\n --popover: oklch(0.205 0 0);\n --popover-foreground: oklch(0.985 0 0);\n --primary: oklch(0.922 0 0);\n --primary-foreground: oklch(0.205 0 0);\n --secondary: oklch(0.269 0 0);\n --secondary-foreground: oklch(0.985 0 0);\n --muted: oklch(0.269 0 0);\n --muted-foreground: oklch(0.708 0 0);\n --accent: oklch(0.269 0 0);\n --accent-foreground: oklch(0.985 0 0);\n --destructive: oklch(0.704 0.191 22.216);\n --border: oklch(1 0 0 / 10%);\n --input: oklch(1 0 0 / 15%);\n --ring: oklch(0.556 0 0);\n --chart-1: oklch(0.488 0.243 264.376);\n --chart-2: oklch(0.696 0.17 162.48);\n --chart-3: oklch(0.769 0.188 70.08);\n --chart-4: oklch(0.627 0.265 303.9);\n --chart-5: oklch(0.645 0.246 16.439);\n --sidebar: oklch(0.205 0 0);\n --sidebar-foreground: oklch(0.985 0 0);\n --sidebar-primary: oklch(0.488 0.243 264.376);\n --sidebar-primary-foreground: oklch(0.985 0 0);\n --sidebar-accent: oklch(0.269 0 0);\n --sidebar-accent-foreground: oklch(0.985 0 0);\n --sidebar-border: oklch(1 0 0 / 10%);\n --sidebar-ring: oklch(0.556 0 0);\n}\n\n@layer base {\n * {\n @apply border-border outline-ring/50;\n }\n body {\n @apply bg-background text-foreground;\n }\n}", + "/app/layout.tsx": "import type { Metadata } from \"next\";\nimport { Geist, Geist_Mono } from \"next/font/google\";\nimport \"./globals.css\";\n\nconst geistSans = Geist({\n variable: \"--font-geist-sans\",\n subsets: [\"latin\"],\n});\n\nconst geistMono = Geist_Mono({\n variable: \"--font-geist-mono\",\n subsets: [\"latin\"],\n});\n\nexport const metadata: Metadata = {\n title: \"Create Next App\",\n description: \"Generated by create next app\",\n};\n\nexport default function RootLayout({\n children,\n}: Readonly<{\n children: React.ReactNode;\n}>) {\n return (\n \n \n {children}\n \n \n );\n}\n", + "/app/page.tsx": "\"use client\";\n\nimport { Button } from \"@/components/ui/button\";\nimport { Input } from \"@/components/ui/input\";\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from \"@/components/ui/select\";\nimport { usePathname, useRouter, useSearchParams } from \"next/navigation\";\nimport { Suspense, useCallback, useState } from \"react\";\nimport DataGrid from \"@/components/DataGrid\";\n\nfunction buildSearchString(\n current: URLSearchParams,\n updates: Record\n) {\n const next = new URLSearchParams(current);\n for (const [key, value] of Object.entries(updates)) {\n if (value === \"\" || value === \"all\") {\n next.delete(key);\n } else {\n next.set(key, value);\n }\n }\n const qs = next.toString();\n return qs ? `?${qs}` : \"\";\n}\n\nfunction HomeContent() {\n const router = useRouter();\n const pathname = usePathname();\n const searchParams = useSearchParams();\n\n const [q, setQ] = useState(searchParams.get(\"q\") ?? \"\");\n const [country, setCountry] = useState(searchParams.get(\"country\") ?? \"\");\n const [status, setStatus] = useState(searchParams.get(\"status\") ?? \"\");\n\n const setParams = useCallback(() => {\n const next = buildSearchString(searchParams, { q, country, status });\n router.replace(next ? `${pathname}${next}` : pathname);\n }, [router, pathname, searchParams, q, country, status]);\n\n return (\n
\n
\n
\n setQ(e.target.value)}\n />\n
\n \n \n \n \n \n All countries\n Canada\n UK\n Germany\n France\n Netherlands\n Switzerland\n Mexico\n \n \n \n \n \n \n \n All\n Active\n Inactive\n \n \n
\n \n
\n
\n
\n \n
\n
\n );\n}\n\nexport default function Home() {\n return (\n }>\n \n \n );\n}\n", + "/components/DataGrid.tsx": "\"use client\";\nimport { useRef, useEffect, memo, forwardRef } from \"react\";\nimport { useSearchParams } from \"next/navigation\";\nimport { HotTable, HotColumn, HotTableRef } from \"@handsontable/react-wrapper\";\nimport { registerTheme } from \"handsontable/themes\";\nimport { registerAllModules } from \"handsontable/registry\";\n\nimport tokensHorizon from 'handsontable/themes/static/variables/tokens/horizon';\n\nimport { colorsShadcn } from \"@/lib/theme/colorsShadcn\";\nimport { iconsShadcn } from \"@/lib/theme/iconsShadcn\";\nimport { data, config } from \"@/lib/helpers\";\n\nregisterAllModules();\n\nconst shadcnDataGridTheme = registerTheme('shadcn-data-grid', {\n icons: iconsShadcn,\n colors: colorsShadcn,\n tokens: tokensHorizon,\n}).params({\n tokens: {\n wrapperBorderRadius: \"var(--radius)\",\n },\n}).setColorScheme(\"light\");\n\nconst DataGrid = forwardRef(function DataGrid(_, ref) {\n return (\n \n \n \n \n \n \n \n \n \n );\n});\n\nconst MemoizedDataGrid = memo(DataGrid);\n\nfunction DataGridWrapper() {\n const hotTableRef = useRef(null);\n const searchParams = useSearchParams();\n\n useEffect(() => {\n const hot = hotTableRef.current?.hotInstance;\n const params = Object.fromEntries(searchParams.entries());\n\n if (hot) {\n const filtersPlugin = hot.getPlugin('filters');\n\n if (filtersPlugin) {\n filtersPlugin.clearConditions();\n\n if (params.q) filtersPlugin.addCondition(0, 'begins_with', [params.q]); // Name\n if (params.country) filtersPlugin.addCondition(2, 'contains', [params.country]); // Country\n if (params.status) filtersPlugin.addCondition(4, 'contains', [params.status === 'active' ? true : false]); // Active\n \n filtersPlugin.filter();\n hot?.render();\n }\n }\n }, [searchParams]);\n\n return (\n \n );\n}\n\nexport default DataGridWrapper;\n", + "/components/ui/button.tsx": "import * as React from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport { Slot } from \"radix-ui\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst buttonVariants = cva(\n \"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive\",\n {\n variants: {\n variant: {\n default: \"bg-primary text-primary-foreground hover:bg-primary/90\",\n destructive:\n \"bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60\",\n outline:\n \"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50\",\n secondary:\n \"bg-secondary text-secondary-foreground hover:bg-secondary/80\",\n ghost:\n \"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50\",\n link: \"text-primary underline-offset-4 hover:underline\",\n },\n size: {\n default: \"h-9 px-4 py-2 has-[>svg]:px-3\",\n xs: \"h-6 gap-1 rounded-md px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3\",\n sm: \"h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5\",\n lg: \"h-10 rounded-md px-6 has-[>svg]:px-4\",\n icon: \"size-9\",\n \"icon-xs\": \"size-6 rounded-md [&_svg:not([class*='size-'])]:size-3\",\n \"icon-sm\": \"size-8\",\n \"icon-lg\": \"size-10\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n }\n)\n\nfunction Button({\n className,\n variant = \"default\",\n size = \"default\",\n asChild = false,\n ...props\n}: React.ComponentProps<\"button\"> &\n VariantProps & {\n asChild?: boolean\n }) {\n const Comp = asChild ? Slot.Root : \"button\"\n\n return (\n \n )\n}\n\nexport { Button, buttonVariants }\n", + "/components/ui/input.tsx": "import * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\nfunction Input({ className, type, ...props }: React.ComponentProps<\"input\">) {\n return (\n \n )\n}\n\nexport { Input }\n", + "/components/ui/select.tsx": "\"use client\"\n\nimport * as React from \"react\"\nimport { CheckIcon, ChevronDownIcon, ChevronUpIcon } from \"lucide-react\"\nimport { Select as SelectPrimitive } from \"radix-ui\"\n\nimport { cn } from \"@/lib/utils\"\n\nfunction Select({\n ...props\n}: React.ComponentProps) {\n return \n}\n\nfunction SelectGroup({\n ...props\n}: React.ComponentProps) {\n return \n}\n\nfunction SelectValue({\n ...props\n}: React.ComponentProps) {\n return \n}\n\nfunction SelectTrigger({\n className,\n size = \"default\",\n children,\n ...props\n}: React.ComponentProps & {\n size?: \"sm\" | \"default\"\n}) {\n return (\n \n {children}\n \n \n \n \n )\n}\n\nfunction SelectContent({\n className,\n children,\n position = \"item-aligned\",\n align = \"center\",\n ...props\n}: React.ComponentProps) {\n return (\n \n \n \n \n {children}\n \n \n \n \n )\n}\n\nfunction SelectLabel({\n className,\n ...props\n}: React.ComponentProps) {\n return (\n \n )\n}\n\nfunction SelectItem({\n className,\n children,\n ...props\n}: React.ComponentProps) {\n return (\n \n \n \n \n \n \n {children}\n \n )\n}\n\nfunction SelectSeparator({\n className,\n ...props\n}: React.ComponentProps) {\n return (\n \n )\n}\n\nfunction SelectScrollUpButton({\n className,\n ...props\n}: React.ComponentProps) {\n return (\n \n \n \n )\n}\n\nfunction SelectScrollDownButton({\n className,\n ...props\n}: React.ComponentProps) {\n return (\n \n \n \n )\n}\n\nexport {\n Select,\n SelectContent,\n SelectGroup,\n SelectItem,\n SelectLabel,\n SelectScrollDownButton,\n SelectScrollUpButton,\n SelectSeparator,\n SelectTrigger,\n SelectValue,\n}\n", + "/components.json": "{\n \"$schema\": \"https://ui.shadcn.com/schema.json\",\n \"style\": \"new-york\",\n \"rsc\": true,\n \"tsx\": true,\n \"tailwind\": {\n \"config\": \"\",\n \"css\": \"app/globals.css\",\n \"baseColor\": \"neutral\",\n \"cssVariables\": true,\n \"prefix\": \"\"\n },\n \"iconLibrary\": \"lucide\",\n \"rtl\": false,\n \"aliases\": {\n \"components\": \"@/components\",\n \"utils\": \"@/lib/utils\",\n \"ui\": \"@/components/ui\",\n \"lib\": \"@/lib\",\n \"hooks\": \"@/hooks\"\n },\n \"registries\": {}\n}\n", + "/eslint.config.mjs": "import { defineConfig, globalIgnores } from \"eslint/config\";\nimport nextVitals from \"eslint-config-next/core-web-vitals\";\nimport nextTs from \"eslint-config-next/typescript\";\n\nconst eslintConfig = defineConfig([\n ...nextVitals,\n ...nextTs,\n // Override default ignores of eslint-config-next.\n globalIgnores([\n // Default ignores of eslint-config-next:\n \".next/**\",\n \"out/**\",\n \"build/**\",\n \"next-env.d.ts\",\n ]),\n]);\n\nexport default eslintConfig;\n", + "/lib/helpers.ts": "import { HotTableProps } from \"@handsontable/react-wrapper\";\n\nexport const config: Partial = {\n width: \"100%\",\n height: \"auto\",\n nestedHeaders: [\n [\n {\n label: \"User\",\n colspan: 4,\n },\n {\n label: \"Account Details\",\n colspan: 3,\n },\n {\n label: \"Login information\",\n colspan: 2,\n },\n ],\n [\n \"Name\",\n \"Age\",\n \"Country\",\n \"City\",\n \"Active\",\n \"Interest\",\n \"Favorite Product\",\n \"Login date\",\n \"Login time\",\n ],\n ],\n cell: [\n {\n row: 1,\n col: 1,\n comment: {\n value: \"A note in a cell\",\n },\n },\n ],\n hiddenColumns: {\n indicators: true,\n },\n hiddenRows: {\n indicators: true,\n },\n contextMenu: [\n \"cut\",\n \"copy\",\n \"---------\",\n \"row_above\",\n \"row_below\",\n \"remove_row\",\n \"---------\",\n \"alignment\",\n \"make_read_only\",\n \"clear_column\",\n ],\n headerClassName: \"htLeft\",\n colHeaders: true,\n collapsibleColumns: true,\n filters: true,\n dropdownMenu: true,\n comments: true,\n multiColumnSorting: true,\n manualColumnResize: true,\n manualRowMove: true,\n manualRowResize: true,\n autoWrapRow: true,\n rowHeaders: true,\n navigableHeaders: true,\n imeFastEdit: true,\n pagination: true,\n licenseKey: \"non-commercial-and-evaluation\",\n};\n\nexport const data = [\n {\n isActive: false,\n name: \"Jerry Moen\",\n age: 22,\n interest: \"Beauty & Personal Care\",\n country: \"Canada\",\n city: \"Toronto\",\n lastLoginDate: \"Nov 25, 2025\",\n lastLoginTime: \"06:02\",\n favoriteProduct: \"Elegant Concrete Hat\",\n },\n {\n isActive: true,\n name: \"Lloyd Auer\",\n age: 22,\n interest: \"Fashion\",\n country: \"Australia\",\n city: \"Sydney\",\n lastLoginDate: \"Dec 06, 2025\",\n lastLoginTime: \"13:24\",\n favoriteProduct: \"Licensed Concrete Pants\",\n },\n {\n isActive: true,\n name: \"Bob Bahringer II\",\n age: 56,\n interest: \"Books & Literature\",\n country: \"UK\",\n city: \"London\",\n lastLoginDate: \"Aug 14, 2025\",\n lastLoginTime: \"08:15\",\n favoriteProduct: \"Rustic Rubber Mouse\",\n },\n {\n isActive: true,\n name: \"Adrian Murphy\",\n age: 58,\n interest: \"Home Decor\",\n country: \"Germany\",\n city: \"Walldorf\",\n lastLoginDate: \"Feb 07, 2025\",\n lastLoginTime: \"06:40\",\n favoriteProduct: \"Handcrafted Granite Car\",\n },\n {\n isActive: true,\n name: \"Juan Yost\",\n age: 26,\n interest: \"Home Decor\",\n country: \"Spain\",\n city: \"Madrid\",\n lastLoginDate: \"Jun 22, 2025\",\n lastLoginTime: \"21:44\",\n favoriteProduct: \"Handmade Bronze Towels\",\n },\n {\n isActive: false,\n name: \"Brandon Dare\",\n age: 32,\n interest: \"Fashion\",\n country: \"China\",\n city: \"Shenzhen\",\n lastLoginDate: \"Jul 20, 2025\",\n lastLoginTime: \"20:12\",\n favoriteProduct: \"Intelligent Soft Soap\",\n },\n {\n isActive: false,\n name: \"Laura Reynolds\",\n age: 38,\n interest: \"Fashion\",\n country: \"France\",\n city: \"Lyon\",\n lastLoginDate: \"Feb 06, 2025\",\n lastLoginTime: \"02:13\",\n favoriteProduct: \"Intelligent Plastic Shoes\",\n },\n {\n isActive: true,\n name: \"Brendan Raynor\",\n age: 60,\n interest: \"Fashion\",\n country: \"Netherlands\",\n city: \"Amsterdam\",\n lastLoginDate: \"Feb 10, 2025\",\n lastLoginTime: \"13:56\",\n favoriteProduct: \"Recycled Concrete Towels\",\n },\n {\n isActive: true,\n name: \"Audrey Blick\",\n age: 50,\n interest: \"Fashion\",\n country: \"Switzerland\",\n city: \"Zurich\",\n lastLoginDate: \"May 19, 2025\",\n lastLoginTime: \"21:40\",\n favoriteProduct: \"Generic Cotton Salad\",\n },\n {\n isActive: true,\n name: \"Jessie Crona V\",\n age: 27,\n interest: \"Home Decor\",\n country: \"Mexico\",\n city: \"Mexico City\",\n lastLoginDate: \"Dec 08, 2025\",\n lastLoginTime: \"17:55\",\n favoriteProduct: \"Luxurious Granite Cheese\",\n },\n {\n isActive: false,\n name: \"Mr. Shaun Wolff\",\n age: 52,\n interest: \"Tech Gadgets\",\n country: \"Canada\",\n city: \"Toronto\",\n lastLoginDate: \"May 02, 2025\",\n lastLoginTime: \"02:16\",\n favoriteProduct: \"Modern Granite Keyboard\",\n },\n {\n isActive: false,\n name: \"Mattie Bergnaum\",\n age: 28,\n interest: \"Fashion\",\n country: \"Canada\",\n city: \"Vancouver\",\n lastLoginDate: \"Jan 25, 2025\",\n lastLoginTime: \"23:08\",\n favoriteProduct: \"Oriental Soft Pizza\",\n },\n];", + "/lib/theme/colorsShadcn.ts": "/**\n * Handsontable theme colors mapped to shadcn CSS variables (globals.css).\n * Structure must match what tokens/main expects: palette (50–950), primary (100–600), white, black, transparent.\n * Uses var(--…) so the grid follows your shadcn theme and dark mode.\n */\nexport const colorsShadcn = {\n palette: {\n 50: \"var(--color-neutral-50)\",\n 100: \"var(--color-neutral-200)\",\n 200: \"var(--color-neutral-100)\",\n 300: \"var(--color-neutral-300)\",\n 400: \"var(--color-neutral-400)\",\n 500: \"var(--color-neutral-500)\",\n 600: \"var(--color-neutral-600)\",\n 700: \"var(--color-neutral-700)\",\n 800: \"var(--color-neutral-800)\",\n 900: \"var(--color-neutral-900)\",\n 950: \"var(--color-neutral-950)\",\n },\n primary: {\n 100: \"var(--primary)\",\n 200: \"var(--primary)\",\n 300: \"var(--primary)\",\n 400: \"var(--color-neutral-900)\",\n 500: \"var(--primary)\",\n 600: \"var(--color-neutral-800)\",\n },\n white: \"var(--background)\",\n black: \"var(--foreground)\",\n transparent: \"transparent\",\n}", + "/lib/theme/iconsShadcn.ts": "/**\n * Handsontable theme icons using Lucide (shadcn) icon set.\n * SVGs use currentColor so they follow shadcn theme. Keys must match VALID_ICON_KEYS.\n */\nconst lucideAttrs =\n 'xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"';\n\nfunction icon(svgContent: string): string {\n return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(`${svgContent}`)}`;\n}\n\n// Lucide: ChevronRight, ChevronLeft, ChevronDown, ChevronUp, ChevronsRight, ChevronsLeft, Check, Menu, Plus, Minus, Circle (filled for radio)\nexport const iconsShadcn = {\n arrowRight: icon(''),\n arrowRightWithBar: icon(''),\n arrowLeft: icon(''),\n arrowLeftWithBar: icon(''),\n arrowDown: icon(''),\n menu: icon(''),\n selectArrow: icon(''),\n arrowNarrowUp: icon(''),\n arrowNarrowDown: icon(''),\n check: icon(''),\n checkbox: icon(''),\n caretHiddenLeft: icon(''),\n caretHiddenRight: icon(''),\n caretHiddenUp: icon(''),\n caretHiddenDown: icon(''),\n collapseOff: icon(''),\n collapseOn: icon(''),\n radio: icon(''),\n} as const;\n", + "/lib/utils.ts": "import { clsx, type ClassValue } from \"clsx\"\nimport { twMerge } from \"tailwind-merge\"\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs))\n}\n", + "/next.config.ts": "import type { NextConfig } from \"next\";\n\nconst nextConfig: NextConfig = {\n devIndicators: false,\n};\n\nexport default nextConfig;\n", + "/package.json": "{\n \"name\": \"my-app\",\n \"version\": \"0.1.0\",\n \"private\": true,\n \"scripts\": {\n \"dev\": \"next dev\",\n \"build\": \"next build\",\n \"start\": \"next start\",\n \"lint\": \"eslint\"\n },\n \"dependencies\": {\n \"@handsontable/react-wrapper\": \"17.0.1\",\n \"handsontable\": \"17.0.1\",\n \"class-variance-authority\": \"^0.7.1\",\n \"clsx\": \"^2.1.1\",\n \"lucide-react\": \"^0.563.0\",\n \"next\": \"16.1.6\",\n \"radix-ui\": \"^1.4.3\",\n \"react\": \"19.2.3\",\n \"react-dom\": \"19.2.3\",\n \"tailwind-merge\": \"^3.4.0\",\n \"tailwindcss\": \"^4\",\n \"@tailwindcss/postcss\": \"^4\",\n \"tw-animate-css\": \"^1.4.0\"\n },\n \"devDependencies\": {\n \"@types/node\": \"^20\",\n \"@types/react\": \"^19\",\n \"@types/react-dom\": \"^19\",\n \"eslint\": \"^9\",\n \"eslint-config-next\": \"16.1.6\",\n \"shadcn\": \"^3.8.4\",\n \"typescript\": \"^5\"\n },\n \"config\": {\n \"codesandbox\": {\n \"templateId\": \"8y5qy5\"\n }\n }\n}\n", + "/postcss.config.mjs": "const config = {\n plugins: {\n \"@tailwindcss/postcss\": {},\n },\n};\n\nexport default config;\n", + "/tsconfig.json": "{\n \"compilerOptions\": {\n \"target\": \"ES2017\",\n \"lib\": [\"dom\", \"dom.iterable\", \"esnext\"],\n \"allowJs\": true,\n \"skipLibCheck\": true,\n \"strict\": true,\n \"noEmit\": true,\n \"esModuleInterop\": true,\n \"module\": \"esnext\",\n \"moduleResolution\": \"bundler\",\n \"resolveJsonModule\": true,\n \"isolatedModules\": true,\n \"jsx\": \"react-jsx\",\n \"incremental\": true,\n \"plugins\": [\n {\n \"name\": \"next\"\n }\n ],\n \"paths\": {\n \"@/*\": [\"./*\"]\n }\n },\n \"include\": [\n \"next-env.d.ts\",\n \"**/*.ts\",\n \"**/*.tsx\",\n \".next/types/**/*.ts\",\n \".next/dev/types/**/*.ts\",\n \"**/*.mts\"\n ],\n \"exclude\": [\"node_modules\"]\n}\n" + } + }, + { + "framework": "astro", + "displayName": "Astro", + "tier": 2, + "sandpackTemplate": null, + "container": "astro", + "htWrappers": [], + "entry": "/src/pages/index.astro", + "htmlEntry": null, + "devCommand": "astro dev --host 0.0.0.0 --port 4321", + "buildCommand": "astro build", + "outputDir": "dist", + "outputGlob": null, + "staticExport": false, + "spaMode": false, + "port": 4321, + "installCommand": "npm install", + "htCoreRange": "18.0.0", + "fileCount": 11, + "assets": [], + "skipped": [], + "files": { + "/.gitignore": "# build output\ndist/\n\n# generated types\n.astro/\n\n# dependencies\nnode_modules/\n\n# logs\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\n\n# environment variables\n.env\n.env.production\n\n# macOS-specific files\n.DS_Store\n\n# jetbrains setting folder\n.idea/\n", + "/README.md": "# Demo\n\n## Description\n\nThis example demonstrates how to set up Handsontable with Astro, providing a foundation for building interactive data-driven applications.\n\n## Installation\n\nInstall all necessary dependencies from npm with `npm install`.\n\n## Development\n\nFirst, run the development server:\n\n```bash\nnpm run dev\n# or\nyarn dev\n# or\npnpm dev\n# or\nbun run dev\n```\n\nYou can then visit localhost:43210 with your browser to view the project.You can start editing the grid by modifying src/components/Grid.astro\n\n## License\n\nHandsontable is a commercial software with two licenses available:\n\n- Free for non-commercial purposes such as teaching, academic research, and evaluation. [Read it here](https://github.com/handsontable/handsontable/blob/master/handsontable-non-commercial-license.pdf).\n- Commercial license with support and maintenance included. See [pricing plans](https://handsontable.com/pricing).\n\n## Get help\n\nJoin our [community](https://github.com/handsontable/handsontable/discussions) to get help, share ideas, and contribute to the ongoing development of Handsontable!\n\nCommercial license holders with an active support plan can contact our [Support Team](https://handsontable.com/contact?category=technical_support).\n", + "/astro.config.mjs": "import { defineConfig } from 'astro/config';\n\n// https://astro.build/config\nexport default defineConfig({\n vite: {\n server: {\n allowedHosts: true, // allow CodeSandbox preview hosts (*.csb.app)\n },\n },\n});\n", + "/package.json": "{\n \"name\": \"@example/basics\",\n \"type\": \"module\",\n \"version\": \"0.0.1\",\n \"private\": true,\n \"scripts\": {\n \"dev\": \"astro dev --port 8080\",\n \"start\": \"astro dev\",\n \"build\": \"astro build\",\n \"preview\": \"astro preview\",\n \"astro\": \"astro\"\n },\n \"dependencies\": {\n \"astro\": \"^4.16.19\",\n \"handsontable\": \"18.0.0\"\n },\n \"config\": {\n \"codesandbox\": {\n \"templateId\": \"438vfj\"\n },\n \"stackblitz\": {\n \"template\": \"node\"\n }\n }\n}\n", + "/public/favicon.svg": "\n \n \n\n", + "/src/components/Grid.astro": "
\n\n", + "/src/data.ts": "export type Data = typeof data;\n\nexport const data = [\n [\n false,\n 'Tagcat',\n 'United Kingdom',\n 'Classic Vest',\n '11/10/2020',\n '01-2331942',\n true,\n '172',\n 2,\n 2,\n ],\n [\n true,\n 'Zoomzone',\n 'Indonesia',\n 'Cycling Cap',\n '03/05/2020',\n '88-2768633',\n true,\n '188',\n 6,\n 2,\n ],\n [\n true,\n 'Meeveo',\n 'United States',\n 'Full-Finger Gloves',\n '27/03/2020',\n '51-6775945',\n true,\n '162',\n 1,\n 3,\n ],\n [\n false,\n 'Buzzdog',\n 'Philippines',\n 'HL Mountain Frame',\n '29/08/2020',\n '44-4028109',\n true,\n '133',\n 7,\n 1,\n ],\n [\n true,\n 'Katz',\n 'India',\n 'Half-Finger Gloves',\n '02/10/2020',\n '08-2758492',\n true,\n '87',\n 1,\n 3,\n ],\n [\n false,\n 'Jaxbean',\n 'China',\n 'HL Road Frame',\n '28/09/2020',\n '84-3557705',\n false,\n '26',\n 8,\n 1,\n ],\n [\n false,\n 'Wikido',\n 'Brazil',\n 'HL Touring Frame',\n '24/06/2020',\n '20-9397637',\n false,\n '110',\n 4,\n 1,\n ],\n [\n false,\n 'Browsedrive',\n 'United States',\n 'LL Mountain Frame',\n '13/03/2020',\n '36-0079556',\n true,\n '50',\n 4,\n 4,\n ],\n [\n false,\n 'Twinder',\n 'United Kingdom',\n 'LL Road Frame',\n '06/04/2020',\n '41-1489542',\n false,\n '160',\n 6,\n 1,\n ],\n [\n false,\n 'Jetwire',\n 'China',\n 'LL Touring Frame',\n '01/02/2020',\n '37-1531629',\n true,\n '30',\n 8,\n 5,\n ],\n [\n false,\n 'Chatterpoint',\n 'China',\n 'Long-Sleeve Logo Jersey',\n '14/07/2020',\n '25-5083429',\n true,\n '39',\n 7,\n 2,\n ],\n [\n false,\n 'Twinder',\n 'Egypt',\n \"Men's Bib-Shorts\",\n '31/08/2020',\n '04-4281278',\n false,\n '96',\n 6,\n 1,\n ],\n [\n false,\n 'Midel',\n 'United States',\n \"Men's Sports Shorts\",\n '27/06/2020',\n '55-1711908',\n true,\n '108',\n 10,\n 3,\n ],\n [\n false,\n 'Yodo',\n 'India',\n 'ML Mountain Frame',\n '16/03/2020',\n '58-8360815',\n false,\n '46',\n 1,\n 1,\n ],\n [\n false,\n 'Camido',\n 'Russia',\n 'ML Mountain Frame-W',\n '13/09/2020',\n '10-3786104',\n true,\n '97',\n 8,\n 3,\n ],\n [\n false,\n 'Eire',\n 'Thailand',\n 'ML Road Frame',\n '10/04/2020',\n '45-1186054',\n true,\n '161',\n 1,\n 4,\n ],\n [\n false,\n 'Vinte',\n 'United Kingdom',\n 'ML Road Frame-W',\n '22/01/2020',\n '62-6202742',\n true,\n '58',\n 4,\n 3,\n ],\n [\n false,\n 'Twitterlist',\n 'China',\n 'Mountain Bike Socks',\n '09/11/2020',\n '88-9646223',\n true,\n '92',\n 8,\n 3,\n ],\n [\n false,\n 'Eidel',\n 'Bangladesh',\n 'Mountain-100',\n '19/09/2020',\n '45-5588112',\n true,\n '5',\n 6,\n 5,\n ],\n [\n false,\n 'Trunyx',\n 'Nigeria',\n 'Mountain-200',\n '09/03/2020',\n '66-6271819',\n true,\n '158',\n 4,\n 1,\n ],\n [\n false,\n 'Katz',\n 'Turkey',\n 'Mountain-300',\n '05/03/2020',\n '38-9245023',\n false,\n '121',\n 5,\n 4,\n ],\n [\n false,\n 'Kaymbo',\n 'United States',\n 'Mountain-400-W',\n '24/12/2020',\n '44-5916927',\n false,\n '61',\n 5,\n 4,\n ],\n [\n false,\n 'Ozu',\n 'Pakistan',\n 'Mountain-500',\n '13/06/2020',\n '31-5449914',\n true,\n '155',\n 2,\n 2,\n ],\n [\n false,\n 'Rhynyx',\n 'India',\n 'Racing Socks',\n '05/12/2020',\n '19-9413869',\n true,\n '162',\n 2,\n 4,\n ],\n [\n false,\n 'Flashset',\n 'Iran',\n 'Road-150',\n '14/12/2020',\n '25-9807605',\n false,\n '46',\n 7,\n 1,\n ],\n [\n false,\n 'Yata',\n 'Congo (Kinshasa)',\n 'Road-250',\n '12/06/2020',\n '74-4291983',\n true,\n '47',\n 4,\n 4,\n ],\n [\n false,\n 'Brainlounge',\n 'Vietnam',\n 'Road-350-W',\n '10/03/2020',\n '83-0980643',\n true,\n '104',\n 2,\n 3,\n ],\n [\n false,\n 'Babblestorm',\n 'United States',\n 'Road-450',\n '10/10/2020',\n '19-2878430',\n true,\n '101',\n 6,\n 4,\n ],\n [\n false,\n 'Youspan',\n 'Brazil',\n 'Road-550-W',\n '16/12/2020',\n '19-1838230',\n true,\n '150',\n 10,\n 3,\n ],\n [\n false,\n 'Nlounge',\n 'China',\n 'Road-650',\n '31/10/2020',\n '32-2267938',\n true,\n '42',\n 4,\n 2,\n ],\n [\n false,\n 'Twinte',\n 'India',\n 'Road-750',\n '17/08/2020',\n '79-2821972',\n true,\n '144',\n 9,\n 3,\n ],\n [\n false,\n 'Oyonder',\n 'United Kingdom',\n 'Short-Sleeve Classic Jersey',\n '04/12/2020',\n '46-6597557',\n true,\n '195',\n 4,\n 1,\n ],\n [\n false,\n 'Gigabox',\n 'Pakistan',\n 'Sport-100',\n '03/02/2020',\n '15-1793960',\n true,\n '199',\n 4,\n 4,\n ],\n [\n false,\n 'Livetube',\n 'France',\n 'Touring-1000',\n '16/05/2020',\n '86-0811003',\n true,\n '110',\n 4,\n 5,\n ],\n [\n false,\n 'Voomm',\n 'United Kingdom',\n 'Touring-2000',\n '15/07/2020',\n '95-3068680',\n true,\n '51',\n 4,\n 4,\n ],\n [\n false,\n 'Voonyx',\n 'China',\n 'Touring-3000',\n '27/11/2020',\n '35-3085360',\n false,\n '69',\n 2,\n 5,\n ],\n [\n false,\n 'Zoombeat',\n 'United States',\n \"Women's Mountain Shorts\",\n '03/11/2020',\n '56-8673088',\n false,\n '53',\n 2,\n 3,\n ],\n [\n false,\n 'Roomm',\n 'China',\n \"Women's Tights\",\n '16/03/2020',\n '76-0085918',\n true,\n '168',\n 1,\n 1,\n ],\n [\n false,\n 'Leenti',\n 'China',\n 'Mountain-400',\n '16/05/2020',\n '03-0893276',\n false,\n '58',\n 1,\n 4,\n ],\n [\n false,\n 'Jetpulse',\n 'United States',\n 'Road-550',\n '08/02/2020',\n '79-9013306',\n true,\n '152',\n 9,\n 3,\n ],\n [\n false,\n 'Katz',\n 'Peru',\n 'Road-350',\n '15/02/2020',\n '55-7799920',\n true,\n '66',\n 4,\n 2,\n ],\n [\n false,\n 'Cogidoo',\n 'India',\n 'LL Mountain Front Wheel',\n '04/06/2020',\n '07-0881122',\n false,\n '112',\n 9,\n 2,\n ],\n [\n false,\n 'Divavu',\n 'Colombia',\n 'Touring Rear Wheel',\n '24/02/2020',\n '58-6157387',\n true,\n '50',\n 10,\n 4,\n ],\n [\n false,\n 'Mydeo',\n 'China',\n 'Touring Front Wheel',\n '07/12/2020',\n '12-2810010',\n false,\n '31',\n 3,\n 5,\n ],\n [\n false,\n 'Browsebug',\n 'Japan',\n 'ML Mountain Front Wheel',\n '14/01/2020',\n '64-9249984',\n true,\n '132',\n 5,\n 5,\n ],\n [\n false,\n 'Layo',\n 'China',\n 'HL Mountain Front Wheel',\n '24/04/2020',\n '45-0739652',\n true,\n '45',\n 1,\n 5,\n ],\n [\n false,\n 'Snaptags',\n 'United Kingdom',\n 'LL Touring Handlebars',\n '06/08/2020',\n '09-5712761',\n true,\n '197',\n 4,\n 2,\n ],\n [\n false,\n 'Cogilith',\n 'China',\n 'HL Touring Handlebars',\n '31/05/2020',\n '01-7345008',\n true,\n '190',\n 4,\n 3,\n ],\n [\n false,\n 'Reallinks',\n 'United Kingdom',\n 'LL Road Front Wheel',\n '14/05/2020',\n '62-1065350',\n true,\n '184',\n 3,\n 4,\n ],\n [\n false,\n 'Quaxo',\n 'United States',\n 'ML Road Front Wheel',\n '23/03/2020',\n '44-7241323',\n true,\n '169',\n 3,\n 4,\n ],\n [\n false,\n 'Devify',\n 'China',\n 'HL Road Front Wheel',\n '12/12/2020',\n '52-0295699',\n false,\n '152',\n 4,\n 4,\n ],\n [\n false,\n 'Youopia',\n 'Angola',\n 'LL Mountain Handlebars',\n '01/04/2020',\n '52-2650922',\n false,\n '182',\n 6,\n 4,\n ],\n [\n false,\n 'Ainyx',\n 'China',\n 'Touring Pedal',\n '27/02/2020',\n '48-3618525',\n true,\n '141',\n 6,\n 1,\n ],\n [\n false,\n 'Browsetype',\n 'Malaysia',\n 'ML Mountain Handlebars',\n '28/04/2020',\n '51-8893923',\n true,\n '169',\n 7,\n 1,\n ],\n [\n false,\n 'Muxo',\n 'China',\n 'HL Mountain Handlebars',\n '22/08/2020',\n '68-5911361',\n false,\n '39',\n 7,\n 1,\n ],\n [\n false,\n 'Bubbletube',\n 'China',\n 'LL Road Handlebars',\n '04/10/2020',\n '41-5880042',\n true,\n '71',\n 8,\n 3,\n ],\n [\n false,\n 'Fadeo',\n 'Vietnam',\n 'ML Road Handlebars',\n '23/04/2020',\n '90-5913983',\n true,\n '148',\n 10,\n 3,\n ],\n [\n false,\n 'Yadel',\n 'United Kingdom',\n 'HL Road Handlebars',\n '18/04/2020',\n '92-0960699',\n true,\n '116',\n 8,\n 1,\n ],\n [\n false,\n 'Blognation',\n 'China',\n 'LL Headset',\n '10/01/2020',\n '06-9493898',\n true,\n '96',\n 10,\n 1,\n ],\n [\n false,\n 'Devpoint',\n 'China',\n 'ML Headset',\n '25/12/2020',\n '69-5878565',\n true,\n '35',\n 4,\n 2,\n ],\n [\n false,\n 'Aibox',\n 'United Kingdom',\n 'HL Headset',\n '18/03/2020',\n '13-1133017',\n true,\n '16',\n 8,\n 2,\n ],\n [\n false,\n 'Brightdog',\n 'China',\n 'LL Mountain Pedal',\n '11/09/2020',\n '39-6530433',\n true,\n '194',\n 2,\n 5,\n ],\n [\n false,\n 'Gabcube',\n 'Nigeria',\n 'ML Mountain Pedal',\n '22/04/2020',\n '96-6860388',\n true,\n '24',\n 1,\n 3,\n ],\n [\n false,\n 'Muxo',\n 'China',\n 'HL Mountain Pedal',\n '05/06/2020',\n '30-0356137',\n true,\n '170',\n 4,\n 4,\n ],\n [\n false,\n 'Tambee',\n 'China',\n 'ML Touring Seat/Saddle',\n '22/02/2020',\n '93-9058255',\n true,\n '184',\n 9,\n 5,\n ],\n [\n false,\n 'Cogilith',\n 'India',\n 'LL Touring Seat/Saddle',\n '06/04/2020',\n '82-9268909',\n false,\n '153',\n 10,\n 4,\n ],\n [\n false,\n 'Dynabox',\n 'Hong Kong',\n 'HL Touring Seat/Saddle',\n '10/01/2020',\n '20-6913815',\n false,\n '88',\n 10,\n 1,\n ],\n [\n false,\n 'Shuffledrive',\n 'Sudan',\n 'LL Road Pedal',\n '16/09/2020',\n '08-8238817',\n true,\n '57',\n 9,\n 2,\n ],\n [\n false,\n 'Fivechat',\n 'China',\n 'ML Road Pedal',\n '26/08/2020',\n '44-7370350',\n false,\n '62',\n 4,\n 1,\n ],\n [\n false,\n 'Meembee',\n 'United States',\n 'HL Road Pedal',\n '27/12/2020',\n '01-3525949',\n true,\n '123',\n 2,\n 4,\n ],\n [\n false,\n 'Dynazzy',\n 'United Kingdom',\n 'LL Mountain Seat/Saddle 1',\n '15/12/2020',\n '04-2414623',\n true,\n '77',\n 10,\n 5,\n ],\n [\n false,\n 'Eare',\n 'China',\n 'ML Mountain Seat/Saddle 1',\n '04/04/2020',\n '15-1917509',\n false,\n '199',\n 9,\n 4,\n ],\n [\n false,\n 'Yozio',\n 'China',\n 'HL Mountain Seat/Saddle 1',\n '15/03/2020',\n '06-2526845',\n true,\n '149',\n 8,\n 2,\n ],\n [\n false,\n 'Quinu',\n \"Xi'an\",\n '425-777-7829',\n '22/02/2020',\n '83-1713558',\n false,\n '191',\n 9,\n 5,\n ],\n [\n false,\n 'Jazzy',\n 'United Kingdom',\n 'ML Road Seat/Saddle 1',\n '07/08/2020',\n '00-8892524',\n true,\n '150',\n 10,\n 2,\n ],\n [\n false,\n 'Thoughtsphere',\n 'China',\n 'HL Road Seat/Saddle 1',\n '28/11/2020',\n '39-5538991',\n true,\n '130',\n 7,\n 3,\n ],\n [\n false,\n 'Leenti',\n 'China',\n 'ML Road Rear Wheel',\n '29/12/2020',\n '06-9002973',\n true,\n '179',\n 1,\n 2,\n ],\n [\n false,\n 'Quaxo',\n 'United Kingdom',\n 'HL Road Rear Wheel',\n '06/09/2020',\n '73-6104901',\n true,\n '98',\n 5,\n 3,\n ],\n [\n false,\n 'Tanoodle',\n 'Chile',\n 'LL Mountain Seat/Saddle 2',\n '24/05/2020',\n '68-7384479',\n true,\n '175',\n 2,\n 3,\n ],\n [\n false,\n 'Feednation',\n 'China',\n 'ML Mountain Seat/Saddle 2',\n '21/11/2020',\n '26-7757763',\n true,\n '11',\n 1,\n 3,\n ],\n [\n false,\n 'Kayveo',\n 'China',\n 'HL Mountain Seat/Saddle 2',\n '21/06/2020',\n '07-4873562',\n false,\n '184',\n 7,\n 4,\n ],\n [\n false,\n 'Meevee',\n 'Saudi Arabia',\n 'LL Road Seat/Saddle 1',\n '16/11/2020',\n '46-5819554',\n false,\n '27',\n 9,\n 3,\n ],\n [\n false,\n 'Twitterworks',\n 'China',\n 'ML Road Seat/Saddle 2',\n '19/04/2020',\n '01-2666826',\n true,\n '186',\n 3,\n 2,\n ],\n [\n false,\n 'Wikizz',\n 'Tanzania',\n 'HL Road Seat/Saddle 2',\n '08/03/2020',\n '54-7090503',\n true,\n '20',\n 3,\n 3,\n ],\n [\n false,\n 'Yoveo',\n 'United States',\n 'LL Mountain Tire',\n '14/10/2020',\n '78-7658520',\n false,\n '153',\n 2,\n 1,\n ],\n [\n false,\n 'Yakidoo',\n 'China',\n 'ML Mountain Tire',\n '12/10/2020',\n '23-9926318',\n true,\n '161',\n 8,\n 5,\n ],\n [\n false,\n 'Oyope',\n 'China',\n 'HL Mountain Tire',\n '20/09/2020',\n '20-0179517',\n true,\n '98',\n 10,\n 5,\n ],\n [\n false,\n 'Skipstorm',\n 'United States',\n 'LL Road Tire',\n '01/10/2020',\n '02-9543343',\n true,\n '30',\n 7,\n 3,\n ],\n [\n false,\n 'Minyx',\n 'United States',\n 'ML Road Tire',\n '07/07/2020',\n '98-3938169',\n true,\n '73',\n 10,\n 2,\n ],\n [\n false,\n 'Miboo',\n 'China',\n 'HL Road Tire',\n '25/07/2020',\n '68-5197934',\n true,\n '158',\n 9,\n 1,\n ],\n [\n false,\n 'Realfire',\n 'United States',\n 'Touring Tire',\n '27/08/2020',\n '39-8260460',\n true,\n '122',\n 5,\n 2,\n ],\n [\n false,\n 'Shufflester',\n 'China',\n 'Mountain Tire Tube',\n '08/06/2020',\n '45-9776170',\n true,\n '33',\n 2,\n 4,\n ],\n [\n false,\n 'Ntag',\n 'China',\n 'Road Tire Tube',\n '06/12/2020',\n '45-0858451',\n true,\n '107',\n 6,\n 2,\n ],\n [\n false,\n 'Jabberbean',\n 'United States',\n 'Touring Tire Tube',\n '26/04/2020',\n '15-4247305',\n true,\n '15',\n 1,\n 2,\n ],\n [\n false,\n 'Thoughtblab',\n 'China',\n 'LL Bottom Bracket',\n '21/05/2020',\n '15-8534931',\n true,\n '168',\n 5,\n 2,\n ],\n [\n false,\n 'Jabbertype',\n 'China',\n 'Classic Vest',\n '25/07/2020',\n '23-1251557',\n true,\n '135',\n 4,\n 2,\n ],\n [\n false,\n 'Buzzshare',\n 'United Kingdom',\n 'Cycling Cap',\n '07/07/2020',\n '86-5920601',\n true,\n '11',\n 1,\n 4,\n ],\n [\n false,\n 'Roodel',\n 'United States',\n 'Full-Finger Gloves',\n '13/01/2020',\n '48-1055459',\n true,\n '41',\n 6,\n 4,\n ],\n [\n false,\n 'Zoovu',\n 'China',\n 'Half-Finger Gloves',\n '03/06/2020',\n '12-7842022',\n true,\n '144',\n 6,\n 1,\n ],\n [\n false,\n 'Photofeed',\n 'China',\n 'HL Mountain Frame',\n '14/07/2020',\n '94-5088099',\n true,\n '106',\n 1,\n 4,\n ],\n];\n", + "/src/env.d.ts": "/// \n/// \n", + "/src/layouts/Layout.astro": "---\ninterface Props {\n\ttitle: string;\n}\n\nconst { title } = Astro.props;\n---\n\n\n\n\t\n\t\t\n\t\t\n\t\t\n\t\t\n\t\t\n\t\t{title}\n\t\n\t\n\t\t
\n\t\t\n\t\t
\n\t\n\n\n", + "/src/pages/index.astro": "---\nimport Layout from '../layouts/Layout.astro';\nimport Grid from '../components/Grid.astro';\n---\n\n\n\t\n\n", + "/tsconfig.json": "{\n \"extends\": \"astro/tsconfigs/base\"\n}\n" + } + }, + { + "framework": "nuxt", + "displayName": "Nuxt 3", + "tier": 2, + "sandpackTemplate": null, + "container": "nuxt", + "htWrappers": [ + "@handsontable/vue3" + ], + "entry": "/app.vue", + "htmlEntry": null, + "devCommand": "nuxt dev -H 0.0.0.0 -p 3000", + "buildCommand": "nuxt generate", + "outputDir": ".output/public", + "outputGlob": null, + "staticExport": false, + "spaMode": false, + "port": 3000, + "installCommand": "npm install", + "htCoreRange": "18.0.0", + "fileCount": 10, + "assets": [ + "public/favicon.ico" + ], + "skipped": [], + "files": { + "/.gitignore": "node_modules\n*.log*\n.nuxt\n.nitro\n.cache\n.output\n.data\n.env\ndist", + "/.nmprc": "engine-strict=true", + "/README.md": "# Demo\n\n## Description\n\nThis example demonstrates how to set up Handsontable with Vue and Nuxt, providing a foundation for building interactive data-driven applications.\n\n## Installation\n\nInstall all necessary dependencies from npm with `npm install`.\n\n## Development\n\nFirst, run the development server:\n\n```bash\nnpm run dev\n# or\nyarn dev\n# or\npnpm dev\n# or\nbun run dev\n```\n\nYou can then visit http://localhost:3000 with your browser to view the project. You can start editing the grid by modifying components/Grid.vue.\n\n## License\n\nHandsontable is a commercial software with two licenses available:\n\n- Free for non-commercial purposes such as teaching, academic research, and evaluation. [Read it here](https://github.com/handsontable/handsontable/blob/master/handsontable-non-commercial-license.pdf).\n- Commercial license with support and maintenance included. See [pricing plans](https://handsontable.com/pricing).\n\n## Get help\n\nJoin our [community](https://github.com/handsontable/handsontable/discussions) to get help, share ideas, and contribute to the ongoing development of Handsontable!\n\nCommercial license holders with an active support plan can contact our [Support Team](https://handsontable.com/contact?category=technical_support).\n", + "/app.vue": "\n\n\n\n\n", + "/components/Grid.vue": "\n\n\n\n", + "/data.ts": "export type Data = typeof data;\n\nexport const data = [\n [\n false,\n 'Tagcat',\n 'United Kingdom',\n 'Classic Vest',\n '11/10/2020',\n '01-2331942',\n true,\n '172',\n 2,\n 2,\n ],\n [\n true,\n 'Zoomzone',\n 'Indonesia',\n 'Cycling Cap',\n '03/05/2020',\n '88-2768633',\n true,\n '188',\n 6,\n 2,\n ],\n [\n true,\n 'Meeveo',\n 'United States',\n 'Full-Finger Gloves',\n '27/03/2020',\n '51-6775945',\n true,\n '162',\n 1,\n 3,\n ],\n [\n false,\n 'Buzzdog',\n 'Philippines',\n 'HL Mountain Frame',\n '29/08/2020',\n '44-4028109',\n true,\n '133',\n 7,\n 1,\n ],\n [\n true,\n 'Katz',\n 'India',\n 'Half-Finger Gloves',\n '02/10/2020',\n '08-2758492',\n true,\n '87',\n 1,\n 3,\n ],\n [\n false,\n 'Jaxbean',\n 'China',\n 'HL Road Frame',\n '28/09/2020',\n '84-3557705',\n false,\n '26',\n 8,\n 1,\n ],\n [\n false,\n 'Wikido',\n 'Brazil',\n 'HL Touring Frame',\n '24/06/2020',\n '20-9397637',\n false,\n '110',\n 4,\n 1,\n ],\n [\n false,\n 'Browsedrive',\n 'United States',\n 'LL Mountain Frame',\n '13/03/2020',\n '36-0079556',\n true,\n '50',\n 4,\n 4,\n ],\n [\n false,\n 'Twinder',\n 'United Kingdom',\n 'LL Road Frame',\n '06/04/2020',\n '41-1489542',\n false,\n '160',\n 6,\n 1,\n ],\n [\n false,\n 'Jetwire',\n 'China',\n 'LL Touring Frame',\n '01/02/2020',\n '37-1531629',\n true,\n '30',\n 8,\n 5,\n ],\n [\n false,\n 'Chatterpoint',\n 'China',\n 'Long-Sleeve Logo Jersey',\n '14/07/2020',\n '25-5083429',\n true,\n '39',\n 7,\n 2,\n ],\n [\n false,\n 'Twinder',\n 'Egypt',\n \"Men's Bib-Shorts\",\n '31/08/2020',\n '04-4281278',\n false,\n '96',\n 6,\n 1,\n ],\n [\n false,\n 'Midel',\n 'United States',\n \"Men's Sports Shorts\",\n '27/06/2020',\n '55-1711908',\n true,\n '108',\n 10,\n 3,\n ],\n [\n false,\n 'Yodo',\n 'India',\n 'ML Mountain Frame',\n '16/03/2020',\n '58-8360815',\n false,\n '46',\n 1,\n 1,\n ],\n [\n false,\n 'Camido',\n 'Russia',\n 'ML Mountain Frame-W',\n '13/09/2020',\n '10-3786104',\n true,\n '97',\n 8,\n 3,\n ],\n [\n false,\n 'Eire',\n 'Thailand',\n 'ML Road Frame',\n '10/04/2020',\n '45-1186054',\n true,\n '161',\n 1,\n 4,\n ],\n [\n false,\n 'Vinte',\n 'United Kingdom',\n 'ML Road Frame-W',\n '22/01/2020',\n '62-6202742',\n true,\n '58',\n 4,\n 3,\n ],\n [\n false,\n 'Twitterlist',\n 'China',\n 'Mountain Bike Socks',\n '09/11/2020',\n '88-9646223',\n true,\n '92',\n 8,\n 3,\n ],\n [\n false,\n 'Eidel',\n 'Bangladesh',\n 'Mountain-100',\n '19/09/2020',\n '45-5588112',\n true,\n '5',\n 6,\n 5,\n ],\n [\n false,\n 'Trunyx',\n 'Nigeria',\n 'Mountain-200',\n '09/03/2020',\n '66-6271819',\n true,\n '158',\n 4,\n 1,\n ],\n [\n false,\n 'Katz',\n 'Turkey',\n 'Mountain-300',\n '05/03/2020',\n '38-9245023',\n false,\n '121',\n 5,\n 4,\n ],\n [\n false,\n 'Kaymbo',\n 'United States',\n 'Mountain-400-W',\n '24/12/2020',\n '44-5916927',\n false,\n '61',\n 5,\n 4,\n ],\n [\n false,\n 'Ozu',\n 'Pakistan',\n 'Mountain-500',\n '13/06/2020',\n '31-5449914',\n true,\n '155',\n 2,\n 2,\n ],\n [\n false,\n 'Rhynyx',\n 'India',\n 'Racing Socks',\n '05/12/2020',\n '19-9413869',\n true,\n '162',\n 2,\n 4,\n ],\n [\n false,\n 'Flashset',\n 'Iran',\n 'Road-150',\n '14/12/2020',\n '25-9807605',\n false,\n '46',\n 7,\n 1,\n ],\n [\n false,\n 'Yata',\n 'Congo (Kinshasa)',\n 'Road-250',\n '12/06/2020',\n '74-4291983',\n true,\n '47',\n 4,\n 4,\n ],\n [\n false,\n 'Brainlounge',\n 'Vietnam',\n 'Road-350-W',\n '10/03/2020',\n '83-0980643',\n true,\n '104',\n 2,\n 3,\n ],\n [\n false,\n 'Babblestorm',\n 'United States',\n 'Road-450',\n '10/10/2020',\n '19-2878430',\n true,\n '101',\n 6,\n 4,\n ],\n [\n false,\n 'Youspan',\n 'Brazil',\n 'Road-550-W',\n '16/12/2020',\n '19-1838230',\n true,\n '150',\n 10,\n 3,\n ],\n [\n false,\n 'Nlounge',\n 'China',\n 'Road-650',\n '31/10/2020',\n '32-2267938',\n true,\n '42',\n 4,\n 2,\n ],\n [\n false,\n 'Twinte',\n 'India',\n 'Road-750',\n '17/08/2020',\n '79-2821972',\n true,\n '144',\n 9,\n 3,\n ],\n [\n false,\n 'Oyonder',\n 'United Kingdom',\n 'Short-Sleeve Classic Jersey',\n '04/12/2020',\n '46-6597557',\n true,\n '195',\n 4,\n 1,\n ],\n [\n false,\n 'Gigabox',\n 'Pakistan',\n 'Sport-100',\n '03/02/2020',\n '15-1793960',\n true,\n '199',\n 4,\n 4,\n ],\n [\n false,\n 'Livetube',\n 'France',\n 'Touring-1000',\n '16/05/2020',\n '86-0811003',\n true,\n '110',\n 4,\n 5,\n ],\n [\n false,\n 'Voomm',\n 'United Kingdom',\n 'Touring-2000',\n '15/07/2020',\n '95-3068680',\n true,\n '51',\n 4,\n 4,\n ],\n [\n false,\n 'Voonyx',\n 'China',\n 'Touring-3000',\n '27/11/2020',\n '35-3085360',\n false,\n '69',\n 2,\n 5,\n ],\n [\n false,\n 'Zoombeat',\n 'United States',\n \"Women's Mountain Shorts\",\n '03/11/2020',\n '56-8673088',\n false,\n '53',\n 2,\n 3,\n ],\n [\n false,\n 'Roomm',\n 'China',\n \"Women's Tights\",\n '16/03/2020',\n '76-0085918',\n true,\n '168',\n 1,\n 1,\n ],\n [\n false,\n 'Leenti',\n 'China',\n 'Mountain-400',\n '16/05/2020',\n '03-0893276',\n false,\n '58',\n 1,\n 4,\n ],\n [\n false,\n 'Jetpulse',\n 'United States',\n 'Road-550',\n '08/02/2020',\n '79-9013306',\n true,\n '152',\n 9,\n 3,\n ],\n [\n false,\n 'Katz',\n 'Peru',\n 'Road-350',\n '15/02/2020',\n '55-7799920',\n true,\n '66',\n 4,\n 2,\n ],\n [\n false,\n 'Cogidoo',\n 'India',\n 'LL Mountain Front Wheel',\n '04/06/2020',\n '07-0881122',\n false,\n '112',\n 9,\n 2,\n ],\n [\n false,\n 'Divavu',\n 'Colombia',\n 'Touring Rear Wheel',\n '24/02/2020',\n '58-6157387',\n true,\n '50',\n 10,\n 4,\n ],\n [\n false,\n 'Mydeo',\n 'China',\n 'Touring Front Wheel',\n '07/12/2020',\n '12-2810010',\n false,\n '31',\n 3,\n 5,\n ],\n [\n false,\n 'Browsebug',\n 'Japan',\n 'ML Mountain Front Wheel',\n '14/01/2020',\n '64-9249984',\n true,\n '132',\n 5,\n 5,\n ],\n [\n false,\n 'Layo',\n 'China',\n 'HL Mountain Front Wheel',\n '24/04/2020',\n '45-0739652',\n true,\n '45',\n 1,\n 5,\n ],\n [\n false,\n 'Snaptags',\n 'United Kingdom',\n 'LL Touring Handlebars',\n '06/08/2020',\n '09-5712761',\n true,\n '197',\n 4,\n 2,\n ],\n [\n false,\n 'Cogilith',\n 'China',\n 'HL Touring Handlebars',\n '31/05/2020',\n '01-7345008',\n true,\n '190',\n 4,\n 3,\n ],\n [\n false,\n 'Reallinks',\n 'United Kingdom',\n 'LL Road Front Wheel',\n '14/05/2020',\n '62-1065350',\n true,\n '184',\n 3,\n 4,\n ],\n [\n false,\n 'Quaxo',\n 'United States',\n 'ML Road Front Wheel',\n '23/03/2020',\n '44-7241323',\n true,\n '169',\n 3,\n 4,\n ],\n [\n false,\n 'Devify',\n 'China',\n 'HL Road Front Wheel',\n '12/12/2020',\n '52-0295699',\n false,\n '152',\n 4,\n 4,\n ],\n [\n false,\n 'Youopia',\n 'Angola',\n 'LL Mountain Handlebars',\n '01/04/2020',\n '52-2650922',\n false,\n '182',\n 6,\n 4,\n ],\n [\n false,\n 'Ainyx',\n 'China',\n 'Touring Pedal',\n '27/02/2020',\n '48-3618525',\n true,\n '141',\n 6,\n 1,\n ],\n [\n false,\n 'Browsetype',\n 'Malaysia',\n 'ML Mountain Handlebars',\n '28/04/2020',\n '51-8893923',\n true,\n '169',\n 7,\n 1,\n ],\n [\n false,\n 'Muxo',\n 'China',\n 'HL Mountain Handlebars',\n '22/08/2020',\n '68-5911361',\n false,\n '39',\n 7,\n 1,\n ],\n [\n false,\n 'Bubbletube',\n 'China',\n 'LL Road Handlebars',\n '04/10/2020',\n '41-5880042',\n true,\n '71',\n 8,\n 3,\n ],\n [\n false,\n 'Fadeo',\n 'Vietnam',\n 'ML Road Handlebars',\n '23/04/2020',\n '90-5913983',\n true,\n '148',\n 10,\n 3,\n ],\n [\n false,\n 'Yadel',\n 'United Kingdom',\n 'HL Road Handlebars',\n '18/04/2020',\n '92-0960699',\n true,\n '116',\n 8,\n 1,\n ],\n [\n false,\n 'Blognation',\n 'China',\n 'LL Headset',\n '10/01/2020',\n '06-9493898',\n true,\n '96',\n 10,\n 1,\n ],\n [\n false,\n 'Devpoint',\n 'China',\n 'ML Headset',\n '25/12/2020',\n '69-5878565',\n true,\n '35',\n 4,\n 2,\n ],\n [\n false,\n 'Aibox',\n 'United Kingdom',\n 'HL Headset',\n '18/03/2020',\n '13-1133017',\n true,\n '16',\n 8,\n 2,\n ],\n [\n false,\n 'Brightdog',\n 'China',\n 'LL Mountain Pedal',\n '11/09/2020',\n '39-6530433',\n true,\n '194',\n 2,\n 5,\n ],\n [\n false,\n 'Gabcube',\n 'Nigeria',\n 'ML Mountain Pedal',\n '22/04/2020',\n '96-6860388',\n true,\n '24',\n 1,\n 3,\n ],\n [\n false,\n 'Muxo',\n 'China',\n 'HL Mountain Pedal',\n '05/06/2020',\n '30-0356137',\n true,\n '170',\n 4,\n 4,\n ],\n [\n false,\n 'Tambee',\n 'China',\n 'ML Touring Seat/Saddle',\n '22/02/2020',\n '93-9058255',\n true,\n '184',\n 9,\n 5,\n ],\n [\n false,\n 'Cogilith',\n 'India',\n 'LL Touring Seat/Saddle',\n '06/04/2020',\n '82-9268909',\n false,\n '153',\n 10,\n 4,\n ],\n [\n false,\n 'Dynabox',\n 'Hong Kong',\n 'HL Touring Seat/Saddle',\n '10/01/2020',\n '20-6913815',\n false,\n '88',\n 10,\n 1,\n ],\n [\n false,\n 'Shuffledrive',\n 'Sudan',\n 'LL Road Pedal',\n '16/09/2020',\n '08-8238817',\n true,\n '57',\n 9,\n 2,\n ],\n [\n false,\n 'Fivechat',\n 'China',\n 'ML Road Pedal',\n '26/08/2020',\n '44-7370350',\n false,\n '62',\n 4,\n 1,\n ],\n [\n false,\n 'Meembee',\n 'United States',\n 'HL Road Pedal',\n '27/12/2020',\n '01-3525949',\n true,\n '123',\n 2,\n 4,\n ],\n [\n false,\n 'Dynazzy',\n 'United Kingdom',\n 'LL Mountain Seat/Saddle 1',\n '15/12/2020',\n '04-2414623',\n true,\n '77',\n 10,\n 5,\n ],\n [\n false,\n 'Eare',\n 'China',\n 'ML Mountain Seat/Saddle 1',\n '04/04/2020',\n '15-1917509',\n false,\n '199',\n 9,\n 4,\n ],\n [\n false,\n 'Yozio',\n 'China',\n 'HL Mountain Seat/Saddle 1',\n '15/03/2020',\n '06-2526845',\n true,\n '149',\n 8,\n 2,\n ],\n [\n false,\n 'Quinu',\n \"Xi'an\",\n '425-777-7829',\n '22/02/2020',\n '83-1713558',\n false,\n '191',\n 9,\n 5,\n ],\n [\n false,\n 'Jazzy',\n 'United Kingdom',\n 'ML Road Seat/Saddle 1',\n '07/08/2020',\n '00-8892524',\n true,\n '150',\n 10,\n 2,\n ],\n [\n false,\n 'Thoughtsphere',\n 'China',\n 'HL Road Seat/Saddle 1',\n '28/11/2020',\n '39-5538991',\n true,\n '130',\n 7,\n 3,\n ],\n [\n false,\n 'Leenti',\n 'China',\n 'ML Road Rear Wheel',\n '29/12/2020',\n '06-9002973',\n true,\n '179',\n 1,\n 2,\n ],\n [\n false,\n 'Quaxo',\n 'United Kingdom',\n 'HL Road Rear Wheel',\n '06/09/2020',\n '73-6104901',\n true,\n '98',\n 5,\n 3,\n ],\n [\n false,\n 'Tanoodle',\n 'Chile',\n 'LL Mountain Seat/Saddle 2',\n '24/05/2020',\n '68-7384479',\n true,\n '175',\n 2,\n 3,\n ],\n [\n false,\n 'Feednation',\n 'China',\n 'ML Mountain Seat/Saddle 2',\n '21/11/2020',\n '26-7757763',\n true,\n '11',\n 1,\n 3,\n ],\n [\n false,\n 'Kayveo',\n 'China',\n 'HL Mountain Seat/Saddle 2',\n '21/06/2020',\n '07-4873562',\n false,\n '184',\n 7,\n 4,\n ],\n [\n false,\n 'Meevee',\n 'Saudi Arabia',\n 'LL Road Seat/Saddle 1',\n '16/11/2020',\n '46-5819554',\n false,\n '27',\n 9,\n 3,\n ],\n [\n false,\n 'Twitterworks',\n 'China',\n 'ML Road Seat/Saddle 2',\n '19/04/2020',\n '01-2666826',\n true,\n '186',\n 3,\n 2,\n ],\n [\n false,\n 'Wikizz',\n 'Tanzania',\n 'HL Road Seat/Saddle 2',\n '08/03/2020',\n '54-7090503',\n true,\n '20',\n 3,\n 3,\n ],\n [\n false,\n 'Yoveo',\n 'United States',\n 'LL Mountain Tire',\n '14/10/2020',\n '78-7658520',\n false,\n '153',\n 2,\n 1,\n ],\n [\n false,\n 'Yakidoo',\n 'China',\n 'ML Mountain Tire',\n '12/10/2020',\n '23-9926318',\n true,\n '161',\n 8,\n 5,\n ],\n [\n false,\n 'Oyope',\n 'China',\n 'HL Mountain Tire',\n '20/09/2020',\n '20-0179517',\n true,\n '98',\n 10,\n 5,\n ],\n [\n false,\n 'Skipstorm',\n 'United States',\n 'LL Road Tire',\n '01/10/2020',\n '02-9543343',\n true,\n '30',\n 7,\n 3,\n ],\n [\n false,\n 'Minyx',\n 'United States',\n 'ML Road Tire',\n '07/07/2020',\n '98-3938169',\n true,\n '73',\n 10,\n 2,\n ],\n [\n false,\n 'Miboo',\n 'China',\n 'HL Road Tire',\n '25/07/2020',\n '68-5197934',\n true,\n '158',\n 9,\n 1,\n ],\n [\n false,\n 'Realfire',\n 'United States',\n 'Touring Tire',\n '27/08/2020',\n '39-8260460',\n true,\n '122',\n 5,\n 2,\n ],\n [\n false,\n 'Shufflester',\n 'China',\n 'Mountain Tire Tube',\n '08/06/2020',\n '45-9776170',\n true,\n '33',\n 2,\n 4,\n ],\n [\n false,\n 'Ntag',\n 'China',\n 'Road Tire Tube',\n '06/12/2020',\n '45-0858451',\n true,\n '107',\n 6,\n 2,\n ],\n [\n false,\n 'Jabberbean',\n 'United States',\n 'Touring Tire Tube',\n '26/04/2020',\n '15-4247305',\n true,\n '15',\n 1,\n 2,\n ],\n [\n false,\n 'Thoughtblab',\n 'China',\n 'LL Bottom Bracket',\n '21/05/2020',\n '15-8534931',\n true,\n '168',\n 5,\n 2,\n ],\n [\n false,\n 'Jabbertype',\n 'China',\n 'Classic Vest',\n '25/07/2020',\n '23-1251557',\n true,\n '135',\n 4,\n 2,\n ],\n [\n false,\n 'Buzzshare',\n 'United Kingdom',\n 'Cycling Cap',\n '07/07/2020',\n '86-5920601',\n true,\n '11',\n 1,\n 4,\n ],\n [\n false,\n 'Roodel',\n 'United States',\n 'Full-Finger Gloves',\n '13/01/2020',\n '48-1055459',\n true,\n '41',\n 6,\n 4,\n ],\n [\n false,\n 'Zoovu',\n 'China',\n 'Half-Finger Gloves',\n '03/06/2020',\n '12-7842022',\n true,\n '144',\n 6,\n 1,\n ],\n [\n false,\n 'Photofeed',\n 'China',\n 'HL Mountain Frame',\n '14/07/2020',\n '94-5088099',\n true,\n '106',\n 1,\n 4,\n ],\n];\n", + "/nuxt.config.ts": "// https://nuxt.com/docs/api/configuration/nuxt-config\nexport default defineNuxtConfig({\n devtools: { enabled: false },\n components: [\n {\n path: '~/components',\n pathPrefix: false,\n },\n ],\n});\n", + "/package.json": "{\n \"private\": true,\n \"type\": \"module\",\n \"scripts\": {\n \"build\": \"nuxt build\",\n \"dev\": \"HOST=0.0.0.0 PORT=8080 nuxt dev\",\n \"generate\": \"nuxt generate\",\n \"preview\": \"nuxt preview\",\n \"postinstall\": \"nuxt prepare\"\n },\n \"dependencies\": {\n \"@handsontable/vue3\": \"18.0.0\",\n \"handsontable\": \"18.0.0\",\n \"nuxt\": \"^3.21.8\"\n },\n \"config\": {\n \"codesandbox\": {\n \"templateId\": \"nj3gp2\"\n }\n }\n}\n", + "/server/tsconfig.json": "{\n \"extends\": \"../.nuxt/tsconfig.server.json\"\n}\n", + "/tsconfig.json": "{\n // https://v3.nuxtjs.org/concepts/typescript\n \"extends\": \"./.nuxt/tsconfig.json\"\n}\n" + } + }, + { + "framework": "remix", + "displayName": "Remix", + "tier": 2, + "sandpackTemplate": null, + "container": "remix", + "htWrappers": [ + "@handsontable/react-wrapper" + ], + "entry": "/app/routes/_index.tsx", + "htmlEntry": null, + "devCommand": "remix vite:dev --host --port 5173", + "buildCommand": "remix vite:build", + "outputDir": "build/client", + "outputGlob": null, + "staticExport": false, + "spaMode": true, + "port": 5173, + "installCommand": "npm install", + "htCoreRange": "^17.1.0", + "fileCount": 12, + "assets": [ + "public/favicon.ico" + ], + "skipped": [], + "files": { + "/.eslintrc.cjs": "/**\n * This is intended to be a basic starting point for linting in your app.\n * It relies on recommended configs out of the box for simplicity, but you can\n * and should modify this configuration to best suit your team's needs.\n */\n\n/** @type {import('eslint').Linter.Config} */\nmodule.exports = {\n root: true,\n parserOptions: {\n ecmaVersion: \"latest\",\n sourceType: \"module\",\n ecmaFeatures: {\n jsx: true,\n },\n },\n env: {\n browser: true,\n commonjs: true,\n es6: true,\n },\n ignorePatterns: [\"!**/.server\", \"!**/.client\"],\n\n // Base config\n extends: [\"eslint:recommended\"],\n\n overrides: [\n // React\n {\n files: [\"**/*.{js,jsx,ts,tsx}\"],\n plugins: [\"react\", \"jsx-a11y\"],\n extends: [\n \"plugin:react/recommended\",\n \"plugin:react/jsx-runtime\",\n \"plugin:react-hooks/recommended\",\n \"plugin:jsx-a11y/recommended\",\n ],\n settings: {\n react: {\n version: \"detect\",\n },\n formComponents: [\"Form\"],\n linkComponents: [\n { name: \"Link\", linkAttribute: \"to\" },\n { name: \"NavLink\", linkAttribute: \"to\" },\n ],\n \"import/resolver\": {\n typescript: {},\n },\n },\n },\n\n // Typescript\n {\n files: [\"**/*.{ts,tsx}\"],\n plugins: [\"@typescript-eslint\", \"import\"],\n parser: \"@typescript-eslint/parser\",\n settings: {\n \"import/internal-regex\": \"^~/\",\n \"import/resolver\": {\n node: {\n extensions: [\".ts\", \".tsx\"],\n },\n typescript: {\n alwaysTryTypes: true,\n },\n },\n },\n extends: [\n \"plugin:@typescript-eslint/recommended\",\n \"plugin:import/recommended\",\n \"plugin:import/typescript\",\n ],\n },\n\n // Node\n {\n files: [\".eslintrc.cjs\"],\n env: {\n node: true,\n },\n },\n ],\n};\n", + "/.gitignore": "node_modules\n\n/.cache\n/build\n.env\n", + "/README.md": "# Demo\n\n## Description\n\nThis example demonstrates how to set up Handsontable with Remix, providing a foundation for building interactive data-driven applications.\n\n## Installation\n\nInstall all necessary dependencies from npm with `npm install`.\n\n## Development\n\nFirst, run the development server:\n\n```bash\nnpm run dev\n# or\nyarn dev\n# or\npnpm dev\n# or\nbun run dev\n```\n\nYou can then visit http://localhost:3000 with your browser to view the project. You can start editing the grid by modifying app/components/Grid.tsx.\n\n## License\n\nHandsontable is a commercial software with two licenses available:\n\n- Free for non-commercial purposes such as teaching, academic research, and evaluation. [Read it here](https://github.com/handsontable/handsontable/blob/master/handsontable-non-commercial-license.pdf).\n- Commercial license with support and maintenance included. See [pricing plans](https://handsontable.com/pricing).\n\n## Get help\n\nJoin our [community](https://github.com/handsontable/handsontable/discussions) to get help, share ideas, and contribute to the ongoing development of Handsontable!\n\nCommercial license holders with an active support plan can contact our [Support Team](https://handsontable.com/contact?category=technical_support).\n", + "/app/components/Grid.tsx": "import {\n AutoColumnSize,\n Autofill,\n ContextMenu,\n CopyPaste,\n DropdownMenu,\n Filters,\n HiddenRows,\n registerPlugin,\n} from \"handsontable/plugins\";\n\nimport {\n CheckboxCellType,\n NumericCellType,\n registerCellType,\n} from \"handsontable/cellTypes\";\nimport { HotTable, HotColumn } from \"@handsontable/react-wrapper\";\n\nimport \"handsontable/styles/handsontable.min.css\";\nimport \"handsontable/styles/ht-theme-main.min.css\";\n\nimport { Data } from \"../../app/data\";\n\nregisterCellType(CheckboxCellType);\nregisterCellType(NumericCellType);\n\nregisterPlugin(AutoColumnSize);\nregisterPlugin(Autofill);\nregisterPlugin(ContextMenu);\nregisterPlugin(CopyPaste);\nregisterPlugin(DropdownMenu);\nregisterPlugin(Filters);\nregisterPlugin(HiddenRows);\n\ntype GridProps = {\n data: Data;\n};\n\nexport default function Grid(props: GridProps) {\n return (\n
\n \n \n \n \n \n \n \n \n \n \n
\n );\n}\n", + "/app/data.ts": "export type Data = typeof data;\n\nexport const data = [\n [\n false,\n 'Tagcat',\n 'United Kingdom',\n 'Classic Vest',\n '11/10/2020',\n '01-2331942',\n true,\n '172',\n 2,\n 2,\n ],\n [\n true,\n 'Zoomzone',\n 'Indonesia',\n 'Cycling Cap',\n '03/05/2020',\n '88-2768633',\n true,\n '188',\n 6,\n 2,\n ],\n [\n true,\n 'Meeveo',\n 'United States',\n 'Full-Finger Gloves',\n '27/03/2020',\n '51-6775945',\n true,\n '162',\n 1,\n 3,\n ],\n [\n false,\n 'Buzzdog',\n 'Philippines',\n 'HL Mountain Frame',\n '29/08/2020',\n '44-4028109',\n true,\n '133',\n 7,\n 1,\n ],\n [\n true,\n 'Katz',\n 'India',\n 'Half-Finger Gloves',\n '02/10/2020',\n '08-2758492',\n true,\n '87',\n 1,\n 3,\n ],\n [\n false,\n 'Jaxbean',\n 'China',\n 'HL Road Frame',\n '28/09/2020',\n '84-3557705',\n false,\n '26',\n 8,\n 1,\n ],\n [\n false,\n 'Wikido',\n 'Brazil',\n 'HL Touring Frame',\n '24/06/2020',\n '20-9397637',\n false,\n '110',\n 4,\n 1,\n ],\n [\n false,\n 'Browsedrive',\n 'United States',\n 'LL Mountain Frame',\n '13/03/2020',\n '36-0079556',\n true,\n '50',\n 4,\n 4,\n ],\n [\n false,\n 'Twinder',\n 'United Kingdom',\n 'LL Road Frame',\n '06/04/2020',\n '41-1489542',\n false,\n '160',\n 6,\n 1,\n ],\n [\n false,\n 'Jetwire',\n 'China',\n 'LL Touring Frame',\n '01/02/2020',\n '37-1531629',\n true,\n '30',\n 8,\n 5,\n ],\n [\n false,\n 'Chatterpoint',\n 'China',\n 'Long-Sleeve Logo Jersey',\n '14/07/2020',\n '25-5083429',\n true,\n '39',\n 7,\n 2,\n ],\n [\n false,\n 'Twinder',\n 'Egypt',\n \"Men's Bib-Shorts\",\n '31/08/2020',\n '04-4281278',\n false,\n '96',\n 6,\n 1,\n ],\n [\n false,\n 'Midel',\n 'United States',\n \"Men's Sports Shorts\",\n '27/06/2020',\n '55-1711908',\n true,\n '108',\n 10,\n 3,\n ],\n [\n false,\n 'Yodo',\n 'India',\n 'ML Mountain Frame',\n '16/03/2020',\n '58-8360815',\n false,\n '46',\n 1,\n 1,\n ],\n [\n false,\n 'Camido',\n 'Russia',\n 'ML Mountain Frame-W',\n '13/09/2020',\n '10-3786104',\n true,\n '97',\n 8,\n 3,\n ],\n [\n false,\n 'Eire',\n 'Thailand',\n 'ML Road Frame',\n '10/04/2020',\n '45-1186054',\n true,\n '161',\n 1,\n 4,\n ],\n [\n false,\n 'Vinte',\n 'United Kingdom',\n 'ML Road Frame-W',\n '22/01/2020',\n '62-6202742',\n true,\n '58',\n 4,\n 3,\n ],\n [\n false,\n 'Twitterlist',\n 'China',\n 'Mountain Bike Socks',\n '09/11/2020',\n '88-9646223',\n true,\n '92',\n 8,\n 3,\n ],\n [\n false,\n 'Eidel',\n 'Bangladesh',\n 'Mountain-100',\n '19/09/2020',\n '45-5588112',\n true,\n '5',\n 6,\n 5,\n ],\n [\n false,\n 'Trunyx',\n 'Nigeria',\n 'Mountain-200',\n '09/03/2020',\n '66-6271819',\n true,\n '158',\n 4,\n 1,\n ],\n [\n false,\n 'Katz',\n 'Turkey',\n 'Mountain-300',\n '05/03/2020',\n '38-9245023',\n false,\n '121',\n 5,\n 4,\n ],\n [\n false,\n 'Kaymbo',\n 'United States',\n 'Mountain-400-W',\n '24/12/2020',\n '44-5916927',\n false,\n '61',\n 5,\n 4,\n ],\n [\n false,\n 'Ozu',\n 'Pakistan',\n 'Mountain-500',\n '13/06/2020',\n '31-5449914',\n true,\n '155',\n 2,\n 2,\n ],\n [\n false,\n 'Rhynyx',\n 'India',\n 'Racing Socks',\n '05/12/2020',\n '19-9413869',\n true,\n '162',\n 2,\n 4,\n ],\n [\n false,\n 'Flashset',\n 'Iran',\n 'Road-150',\n '14/12/2020',\n '25-9807605',\n false,\n '46',\n 7,\n 1,\n ],\n [\n false,\n 'Yata',\n 'Congo (Kinshasa)',\n 'Road-250',\n '12/06/2020',\n '74-4291983',\n true,\n '47',\n 4,\n 4,\n ],\n [\n false,\n 'Brainlounge',\n 'Vietnam',\n 'Road-350-W',\n '10/03/2020',\n '83-0980643',\n true,\n '104',\n 2,\n 3,\n ],\n [\n false,\n 'Babblestorm',\n 'United States',\n 'Road-450',\n '10/10/2020',\n '19-2878430',\n true,\n '101',\n 6,\n 4,\n ],\n [\n false,\n 'Youspan',\n 'Brazil',\n 'Road-550-W',\n '16/12/2020',\n '19-1838230',\n true,\n '150',\n 10,\n 3,\n ],\n [\n false,\n 'Nlounge',\n 'China',\n 'Road-650',\n '31/10/2020',\n '32-2267938',\n true,\n '42',\n 4,\n 2,\n ],\n [\n false,\n 'Twinte',\n 'India',\n 'Road-750',\n '17/08/2020',\n '79-2821972',\n true,\n '144',\n 9,\n 3,\n ],\n [\n false,\n 'Oyonder',\n 'United Kingdom',\n 'Short-Sleeve Classic Jersey',\n '04/12/2020',\n '46-6597557',\n true,\n '195',\n 4,\n 1,\n ],\n [\n false,\n 'Gigabox',\n 'Pakistan',\n 'Sport-100',\n '03/02/2020',\n '15-1793960',\n true,\n '199',\n 4,\n 4,\n ],\n [\n false,\n 'Livetube',\n 'France',\n 'Touring-1000',\n '16/05/2020',\n '86-0811003',\n true,\n '110',\n 4,\n 5,\n ],\n [\n false,\n 'Voomm',\n 'United Kingdom',\n 'Touring-2000',\n '15/07/2020',\n '95-3068680',\n true,\n '51',\n 4,\n 4,\n ],\n [\n false,\n 'Voonyx',\n 'China',\n 'Touring-3000',\n '27/11/2020',\n '35-3085360',\n false,\n '69',\n 2,\n 5,\n ],\n [\n false,\n 'Zoombeat',\n 'United States',\n \"Women's Mountain Shorts\",\n '03/11/2020',\n '56-8673088',\n false,\n '53',\n 2,\n 3,\n ],\n [\n false,\n 'Roomm',\n 'China',\n \"Women's Tights\",\n '16/03/2020',\n '76-0085918',\n true,\n '168',\n 1,\n 1,\n ],\n [\n false,\n 'Leenti',\n 'China',\n 'Mountain-400',\n '16/05/2020',\n '03-0893276',\n false,\n '58',\n 1,\n 4,\n ],\n [\n false,\n 'Jetpulse',\n 'United States',\n 'Road-550',\n '08/02/2020',\n '79-9013306',\n true,\n '152',\n 9,\n 3,\n ],\n [\n false,\n 'Katz',\n 'Peru',\n 'Road-350',\n '15/02/2020',\n '55-7799920',\n true,\n '66',\n 4,\n 2,\n ],\n [\n false,\n 'Cogidoo',\n 'India',\n 'LL Mountain Front Wheel',\n '04/06/2020',\n '07-0881122',\n false,\n '112',\n 9,\n 2,\n ],\n [\n false,\n 'Divavu',\n 'Colombia',\n 'Touring Rear Wheel',\n '24/02/2020',\n '58-6157387',\n true,\n '50',\n 10,\n 4,\n ],\n [\n false,\n 'Mydeo',\n 'China',\n 'Touring Front Wheel',\n '07/12/2020',\n '12-2810010',\n false,\n '31',\n 3,\n 5,\n ],\n [\n false,\n 'Browsebug',\n 'Japan',\n 'ML Mountain Front Wheel',\n '14/01/2020',\n '64-9249984',\n true,\n '132',\n 5,\n 5,\n ],\n [\n false,\n 'Layo',\n 'China',\n 'HL Mountain Front Wheel',\n '24/04/2020',\n '45-0739652',\n true,\n '45',\n 1,\n 5,\n ],\n [\n false,\n 'Snaptags',\n 'United Kingdom',\n 'LL Touring Handlebars',\n '06/08/2020',\n '09-5712761',\n true,\n '197',\n 4,\n 2,\n ],\n [\n false,\n 'Cogilith',\n 'China',\n 'HL Touring Handlebars',\n '31/05/2020',\n '01-7345008',\n true,\n '190',\n 4,\n 3,\n ],\n [\n false,\n 'Reallinks',\n 'United Kingdom',\n 'LL Road Front Wheel',\n '14/05/2020',\n '62-1065350',\n true,\n '184',\n 3,\n 4,\n ],\n [\n false,\n 'Quaxo',\n 'United States',\n 'ML Road Front Wheel',\n '23/03/2020',\n '44-7241323',\n true,\n '169',\n 3,\n 4,\n ],\n [\n false,\n 'Devify',\n 'China',\n 'HL Road Front Wheel',\n '12/12/2020',\n '52-0295699',\n false,\n '152',\n 4,\n 4,\n ],\n [\n false,\n 'Youopia',\n 'Angola',\n 'LL Mountain Handlebars',\n '01/04/2020',\n '52-2650922',\n false,\n '182',\n 6,\n 4,\n ],\n [\n false,\n 'Ainyx',\n 'China',\n 'Touring Pedal',\n '27/02/2020',\n '48-3618525',\n true,\n '141',\n 6,\n 1,\n ],\n [\n false,\n 'Browsetype',\n 'Malaysia',\n 'ML Mountain Handlebars',\n '28/04/2020',\n '51-8893923',\n true,\n '169',\n 7,\n 1,\n ],\n [\n false,\n 'Muxo',\n 'China',\n 'HL Mountain Handlebars',\n '22/08/2020',\n '68-5911361',\n false,\n '39',\n 7,\n 1,\n ],\n [\n false,\n 'Bubbletube',\n 'China',\n 'LL Road Handlebars',\n '04/10/2020',\n '41-5880042',\n true,\n '71',\n 8,\n 3,\n ],\n [\n false,\n 'Fadeo',\n 'Vietnam',\n 'ML Road Handlebars',\n '23/04/2020',\n '90-5913983',\n true,\n '148',\n 10,\n 3,\n ],\n [\n false,\n 'Yadel',\n 'United Kingdom',\n 'HL Road Handlebars',\n '18/04/2020',\n '92-0960699',\n true,\n '116',\n 8,\n 1,\n ],\n [\n false,\n 'Blognation',\n 'China',\n 'LL Headset',\n '10/01/2020',\n '06-9493898',\n true,\n '96',\n 10,\n 1,\n ],\n [\n false,\n 'Devpoint',\n 'China',\n 'ML Headset',\n '25/12/2020',\n '69-5878565',\n true,\n '35',\n 4,\n 2,\n ],\n [\n false,\n 'Aibox',\n 'United Kingdom',\n 'HL Headset',\n '18/03/2020',\n '13-1133017',\n true,\n '16',\n 8,\n 2,\n ],\n [\n false,\n 'Brightdog',\n 'China',\n 'LL Mountain Pedal',\n '11/09/2020',\n '39-6530433',\n true,\n '194',\n 2,\n 5,\n ],\n [\n false,\n 'Gabcube',\n 'Nigeria',\n 'ML Mountain Pedal',\n '22/04/2020',\n '96-6860388',\n true,\n '24',\n 1,\n 3,\n ],\n [\n false,\n 'Muxo',\n 'China',\n 'HL Mountain Pedal',\n '05/06/2020',\n '30-0356137',\n true,\n '170',\n 4,\n 4,\n ],\n [\n false,\n 'Tambee',\n 'China',\n 'ML Touring Seat/Saddle',\n '22/02/2020',\n '93-9058255',\n true,\n '184',\n 9,\n 5,\n ],\n [\n false,\n 'Cogilith',\n 'India',\n 'LL Touring Seat/Saddle',\n '06/04/2020',\n '82-9268909',\n false,\n '153',\n 10,\n 4,\n ],\n [\n false,\n 'Dynabox',\n 'Hong Kong',\n 'HL Touring Seat/Saddle',\n '10/01/2020',\n '20-6913815',\n false,\n '88',\n 10,\n 1,\n ],\n [\n false,\n 'Shuffledrive',\n 'Sudan',\n 'LL Road Pedal',\n '16/09/2020',\n '08-8238817',\n true,\n '57',\n 9,\n 2,\n ],\n [\n false,\n 'Fivechat',\n 'China',\n 'ML Road Pedal',\n '26/08/2020',\n '44-7370350',\n false,\n '62',\n 4,\n 1,\n ],\n [\n false,\n 'Meembee',\n 'United States',\n 'HL Road Pedal',\n '27/12/2020',\n '01-3525949',\n true,\n '123',\n 2,\n 4,\n ],\n [\n false,\n 'Dynazzy',\n 'United Kingdom',\n 'LL Mountain Seat/Saddle 1',\n '15/12/2020',\n '04-2414623',\n true,\n '77',\n 10,\n 5,\n ],\n [\n false,\n 'Eare',\n 'China',\n 'ML Mountain Seat/Saddle 1',\n '04/04/2020',\n '15-1917509',\n false,\n '199',\n 9,\n 4,\n ],\n [\n false,\n 'Yozio',\n 'China',\n 'HL Mountain Seat/Saddle 1',\n '15/03/2020',\n '06-2526845',\n true,\n '149',\n 8,\n 2,\n ],\n [\n false,\n 'Quinu',\n \"Xi'an\",\n '425-777-7829',\n '22/02/2020',\n '83-1713558',\n false,\n '191',\n 9,\n 5,\n ],\n [\n false,\n 'Jazzy',\n 'United Kingdom',\n 'ML Road Seat/Saddle 1',\n '07/08/2020',\n '00-8892524',\n true,\n '150',\n 10,\n 2,\n ],\n [\n false,\n 'Thoughtsphere',\n 'China',\n 'HL Road Seat/Saddle 1',\n '28/11/2020',\n '39-5538991',\n true,\n '130',\n 7,\n 3,\n ],\n [\n false,\n 'Leenti',\n 'China',\n 'ML Road Rear Wheel',\n '29/12/2020',\n '06-9002973',\n true,\n '179',\n 1,\n 2,\n ],\n [\n false,\n 'Quaxo',\n 'United Kingdom',\n 'HL Road Rear Wheel',\n '06/09/2020',\n '73-6104901',\n true,\n '98',\n 5,\n 3,\n ],\n [\n false,\n 'Tanoodle',\n 'Chile',\n 'LL Mountain Seat/Saddle 2',\n '24/05/2020',\n '68-7384479',\n true,\n '175',\n 2,\n 3,\n ],\n [\n false,\n 'Feednation',\n 'China',\n 'ML Mountain Seat/Saddle 2',\n '21/11/2020',\n '26-7757763',\n true,\n '11',\n 1,\n 3,\n ],\n [\n false,\n 'Kayveo',\n 'China',\n 'HL Mountain Seat/Saddle 2',\n '21/06/2020',\n '07-4873562',\n false,\n '184',\n 7,\n 4,\n ],\n [\n false,\n 'Meevee',\n 'Saudi Arabia',\n 'LL Road Seat/Saddle 1',\n '16/11/2020',\n '46-5819554',\n false,\n '27',\n 9,\n 3,\n ],\n [\n false,\n 'Twitterworks',\n 'China',\n 'ML Road Seat/Saddle 2',\n '19/04/2020',\n '01-2666826',\n true,\n '186',\n 3,\n 2,\n ],\n [\n false,\n 'Wikizz',\n 'Tanzania',\n 'HL Road Seat/Saddle 2',\n '08/03/2020',\n '54-7090503',\n true,\n '20',\n 3,\n 3,\n ],\n [\n false,\n 'Yoveo',\n 'United States',\n 'LL Mountain Tire',\n '14/10/2020',\n '78-7658520',\n false,\n '153',\n 2,\n 1,\n ],\n [\n false,\n 'Yakidoo',\n 'China',\n 'ML Mountain Tire',\n '12/10/2020',\n '23-9926318',\n true,\n '161',\n 8,\n 5,\n ],\n [\n false,\n 'Oyope',\n 'China',\n 'HL Mountain Tire',\n '20/09/2020',\n '20-0179517',\n true,\n '98',\n 10,\n 5,\n ],\n [\n false,\n 'Skipstorm',\n 'United States',\n 'LL Road Tire',\n '01/10/2020',\n '02-9543343',\n true,\n '30',\n 7,\n 3,\n ],\n [\n false,\n 'Minyx',\n 'United States',\n 'ML Road Tire',\n '07/07/2020',\n '98-3938169',\n true,\n '73',\n 10,\n 2,\n ],\n [\n false,\n 'Miboo',\n 'China',\n 'HL Road Tire',\n '25/07/2020',\n '68-5197934',\n true,\n '158',\n 9,\n 1,\n ],\n [\n false,\n 'Realfire',\n 'United States',\n 'Touring Tire',\n '27/08/2020',\n '39-8260460',\n true,\n '122',\n 5,\n 2,\n ],\n [\n false,\n 'Shufflester',\n 'China',\n 'Mountain Tire Tube',\n '08/06/2020',\n '45-9776170',\n true,\n '33',\n 2,\n 4,\n ],\n [\n false,\n 'Ntag',\n 'China',\n 'Road Tire Tube',\n '06/12/2020',\n '45-0858451',\n true,\n '107',\n 6,\n 2,\n ],\n [\n false,\n 'Jabberbean',\n 'United States',\n 'Touring Tire Tube',\n '26/04/2020',\n '15-4247305',\n true,\n '15',\n 1,\n 2,\n ],\n [\n false,\n 'Thoughtblab',\n 'China',\n 'LL Bottom Bracket',\n '21/05/2020',\n '15-8534931',\n true,\n '168',\n 5,\n 2,\n ],\n [\n false,\n 'Jabbertype',\n 'China',\n 'Classic Vest',\n '25/07/2020',\n '23-1251557',\n true,\n '135',\n 4,\n 2,\n ],\n [\n false,\n 'Buzzshare',\n 'United Kingdom',\n 'Cycling Cap',\n '07/07/2020',\n '86-5920601',\n true,\n '11',\n 1,\n 4,\n ],\n [\n false,\n 'Roodel',\n 'United States',\n 'Full-Finger Gloves',\n '13/01/2020',\n '48-1055459',\n true,\n '41',\n 6,\n 4,\n ],\n [\n false,\n 'Zoovu',\n 'China',\n 'Half-Finger Gloves',\n '03/06/2020',\n '12-7842022',\n true,\n '144',\n 6,\n 1,\n ],\n [\n false,\n 'Photofeed',\n 'China',\n 'HL Mountain Frame',\n '14/07/2020',\n '94-5088099',\n true,\n '106',\n 1,\n 4,\n ],\n];\n", + "/app/entry.client.tsx": "/**\n * By default, Remix will handle hydrating your app on the client for you.\n * You are free to delete this file if you'd like to, but if you ever want it revealed again, you can run `npx remix reveal` ✨\n * For more information, see https://remix.run/file-conventions/entry.client\n */\n\nimport { RemixBrowser } from '@remix-run/react';\nimport { startTransition, StrictMode } from 'react';\nimport { hydrateRoot } from 'react-dom/client';\n\nstartTransition(() => {\n hydrateRoot(\n document,\n \n \n \n );\n});\n", + "/app/entry.server.tsx": "/**\n * By default, Remix will handle generating the HTTP Response for you.\n * You are free to delete this file if you'd like to, but if you ever want it revealed again, you can run `npx remix reveal` ✨\n * For more information, see https://remix.run/file-conventions/entry.server\n */\n\nimport { PassThrough } from 'node:stream';\n\nimport type { AppLoadContext, EntryContext } from '@remix-run/node';\nimport { createReadableStreamFromReadable } from '@remix-run/node';\nimport { RemixServer } from '@remix-run/react';\nimport { isbot } from 'isbot';\nimport { renderToPipeableStream } from 'react-dom/server';\n\nconst ABORT_DELAY = 5_000;\n\nexport default function handleRequest(\n request: Request,\n responseStatusCode: number,\n responseHeaders: Headers,\n remixContext: EntryContext,\n // This is ignored so we can keep it in the template for visibility. Feel\n // free to delete this parameter in your app if you're not using it!\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n loadContext: AppLoadContext\n) {\n return isbot(request.headers.get('user-agent') || '')\n ? handleBotRequest(\n request,\n responseStatusCode,\n responseHeaders,\n remixContext\n )\n : handleBrowserRequest(\n request,\n responseStatusCode,\n responseHeaders,\n remixContext\n );\n}\n\nfunction handleBotRequest(\n request: Request,\n responseStatusCode: number,\n responseHeaders: Headers,\n remixContext: EntryContext\n) {\n return new Promise((resolve, reject) => {\n let shellRendered = false;\n const { pipe, abort } = renderToPipeableStream(\n ,\n {\n onAllReady() {\n shellRendered = true;\n const body = new PassThrough();\n const stream = createReadableStreamFromReadable(body);\n\n responseHeaders.set('Content-Type', 'text/html');\n\n resolve(\n new Response(stream, {\n headers: responseHeaders,\n status: responseStatusCode,\n })\n );\n\n pipe(body);\n },\n onShellError(error: unknown) {\n reject(error);\n },\n onError(error: unknown) {\n responseStatusCode = 500;\n // Log streaming rendering errors from inside the shell. Don't log\n // errors encountered during initial shell rendering since they'll\n // reject and get logged in handleDocumentRequest.\n if (shellRendered) {\n console.error(error);\n }\n },\n }\n );\n\n setTimeout(abort, ABORT_DELAY);\n });\n}\n\nfunction handleBrowserRequest(\n request: Request,\n responseStatusCode: number,\n responseHeaders: Headers,\n remixContext: EntryContext\n) {\n return new Promise((resolve, reject) => {\n let shellRendered = false;\n const { pipe, abort } = renderToPipeableStream(\n ,\n {\n onShellReady() {\n shellRendered = true;\n const body = new PassThrough();\n const stream = createReadableStreamFromReadable(body);\n\n responseHeaders.set('Content-Type', 'text/html');\n\n resolve(\n new Response(stream, {\n headers: responseHeaders,\n status: responseStatusCode,\n })\n );\n\n pipe(body);\n },\n onShellError(error: unknown) {\n reject(error);\n },\n onError(error: unknown) {\n responseStatusCode = 500;\n // Log streaming rendering errors from inside the shell. Don't log\n // errors encountered during initial shell rendering since they'll\n // reject and get logged in handleDocumentRequest.\n if (shellRendered) {\n console.error(error);\n }\n },\n }\n );\n\n setTimeout(abort, ABORT_DELAY);\n });\n}\n", + "/app/root.tsx": "import {\n Links,\n Meta,\n Outlet,\n Scripts,\n ScrollRestoration,\n} from '@remix-run/react';\n\nexport function Layout({ children }: { children: React.ReactNode }) {\n return (\n \n \n \n \n \n \n \n \n {children}\n \n \n \n \n );\n}\n\nexport default function App() {\n return ;\n}\n", + "/app/routes/_index.tsx": "import Grid from '~/components/Grid';\nimport { data } from '../data';\n\nexport default function Index() {\n return (\n \n \n \n );\n}\n", + "/package.json": "{\n \"private\": true,\n \"sideEffects\": false,\n \"type\": \"module\",\n \"scripts\": {\n \"build\": \"remix vite:build\",\n \"dev\": \"remix vite:dev\",\n \"lint\": \"eslint --ignore-path .gitignore --cache --cache-location ./node_modules/.cache/eslint .\",\n \"start\": \"remix-serve ./build/server/index.js\",\n \"typecheck\": \"tsc\"\n },\n \"dependencies\": {\n \"@remix-run/node\": \"^2.17.5\",\n \"@remix-run/react\": \"^2.17.5\",\n \"@remix-run/serve\": \"^2.17.5\",\n \"isbot\": \"^4.1.0\",\n \"react\": \"^18.2.0\",\n \"react-dom\": \"^18.2.0\",\n \"@handsontable/react-wrapper\": \"^17.1.0\",\n \"handsontable\": \"^17.1.0\"\n },\n \"devDependencies\": {\n \"@remix-run/dev\": \"*\",\n \"@types/react\": \"^18.2.20\",\n \"@types/react-dom\": \"^18.2.7\",\n \"@typescript-eslint/eslint-plugin\": \"^6.7.4\",\n \"@typescript-eslint/parser\": \"^6.7.4\",\n \"eslint\": \"^8.38.0\",\n \"eslint-import-resolver-typescript\": \"^3.6.1\",\n \"eslint-plugin-import\": \"^2.28.1\",\n \"eslint-plugin-jsx-a11y\": \"^6.7.1\",\n \"eslint-plugin-react\": \"^7.33.2\",\n \"eslint-plugin-react-hooks\": \"^4.6.0\",\n \"typescript\": \"^5.1.6\",\n \"vite\": \"^5.4.21\",\n \"vite-tsconfig-paths\": \"^4.2.1\"\n },\n \"engines\": {\n \"node\": \">=18.0.0\"\n },\n \"config\": {\n \"codesandbox\": {\n \"templateId\": \"m4k8vw\"\n },\n \"stackblitz\": {\n \"template\": \"node\"\n }\n }\n}\n", + "/tsconfig.json": "{\n \"include\": [\n \"**/*.ts\",\n \"**/*.tsx\",\n \"**/.server/**/*.ts\",\n \"**/.server/**/*.tsx\",\n \"**/.client/**/*.ts\",\n \"**/.client/**/*.tsx\"\n ],\n \"compilerOptions\": {\n \"lib\": [\"DOM\", \"DOM.Iterable\", \"ES2022\"],\n \"types\": [\"@remix-run/node\", \"vite/client\"],\n \"isolatedModules\": true,\n \"esModuleInterop\": true,\n \"jsx\": \"react-jsx\",\n \"module\": \"ESNext\",\n \"moduleResolution\": \"Bundler\",\n \"resolveJsonModule\": true,\n \"target\": \"ES2022\",\n \"strict\": true,\n \"allowJs\": true,\n \"skipLibCheck\": true,\n \"forceConsistentCasingInFileNames\": true,\n \"baseUrl\": \".\",\n \"paths\": {\n \"~/*\": [\"./app/*\"]\n },\n\n // Vite takes care of building everything, not tsc.\n \"noEmit\": true\n }\n}\n", + "/vite.config.ts": "import { vitePlugin as remix } from \"@remix-run/dev\";\nimport { installGlobals } from \"@remix-run/node\";\nimport { defineConfig } from \"vite\";\nimport tsconfigPaths from \"vite-tsconfig-paths\";\n\ninstallGlobals();\n\nexport default defineConfig({\n plugins: [remix(), tsconfigPaths()],\n server: {\n allowedHosts: true,\n }\n});\n" + } + } + ] +} diff --git a/runner/config/frameworks.json b/runner/config/frameworks.json new file mode 100644 index 0000000..0c6a7d1 --- /dev/null +++ b/runner/config/frameworks.json @@ -0,0 +1,174 @@ +{ + "$schema": "./frameworks.schema.json", + "_comment": "Single source of truth for the 13 Handsontable examples. Consumed by pipeline/import.mjs (catalog generation), packages/runtime (applyHandsontableVersion + resolveRuntime), workers/api (build orchestration), and containers/* (Tier-2 images). tier 1 = client-side (Sandpack, in-browser bundler); tier 2 = SSR/meta-framework (Cloudflare Sandbox container running the real dev server). htWrappers lists the Handsontable framework wrapper packages that must be pinned in lockstep with core `handsontable`; `@handsontable/pikaday` is intentionally NEVER bumped. Supported Handsontable major range: 15-19.", + "frameworks": { + "example1": { + "tier": 1, + "displayName": "Vanilla (Parcel)", + "sandpackTemplate": "vanilla-ts", + "htWrappers": [], + "entry": "index.ts", + "htmlEntry": "index.html", + "devCommand": null, + "buildCommand": "parcel build index.html", + "outputDir": "dist", + "port": null, + "installCommand": "npm install" + }, + "javascript": { + "tier": 1, + "displayName": "JavaScript (Vite)", + "sandpackTemplate": "vanilla", + "htWrappers": [], + "entry": "index.js", + "htmlEntry": "index.html", + "devCommand": null, + "buildCommand": "vite build", + "outputDir": "dist", + "port": null, + "installCommand": "npm install" + }, + "typescript": { + "tier": 1, + "displayName": "TypeScript (Vite)", + "sandpackTemplate": "vanilla-ts", + "htWrappers": [], + "entry": "index.ts", + "htmlEntry": "index.html", + "devCommand": null, + "buildCommand": "tsc && vite build", + "outputDir": "dist", + "port": null, + "installCommand": "npm install" + }, + "react": { + "tier": 1, + "displayName": "React (Vite, TS)", + "sandpackTemplate": "react-ts", + "htWrappers": ["@handsontable/react-wrapper"], + "entry": "src/index.tsx", + "htmlEntry": "index.html", + "devCommand": null, + "buildCommand": "tsc -b && vite build", + "outputDir": "dist", + "port": null, + "installCommand": "npm install" + }, + "react-js": { + "tier": 1, + "displayName": "React (Vite, JS)", + "sandpackTemplate": "react", + "htWrappers": ["@handsontable/react-wrapper"], + "entry": "index.jsx", + "htmlEntry": "index.html", + "devCommand": null, + "buildCommand": "vite build", + "outputDir": "dist", + "port": null, + "installCommand": "npm install" + }, + "ant-design": { + "tier": 1, + "displayName": "Ant Design + React", + "sandpackTemplate": "react", + "htWrappers": ["@handsontable/react-wrapper"], + "entry": "src/index.jsx", + "htmlEntry": "index.html", + "devCommand": null, + "buildCommand": "vite build", + "outputDir": "dist", + "port": null, + "installCommand": "npm install" + }, + "vue": { + "tier": 1, + "displayName": "Vue 3 (Vite)", + "sandpackTemplate": "vue-ts", + "htWrappers": ["@handsontable/vue3"], + "entry": "src/main.ts", + "htmlEntry": "index.html", + "devCommand": null, + "buildCommand": "vite build", + "outputDir": "dist", + "port": null, + "installCommand": "npm install" + }, + "angular": { + "tier": 2, + "displayName": "Angular", + "container": "angular", + "htWrappers": ["@handsontable/angular-wrapper"], + "entry": "src/main.ts", + "htmlEntry": "src/index.html", + "devCommand": "npm run start -- --host 0.0.0.0 --disable-host-check --port 3000", + "buildCommand": "ng build", + "outputDir": "dist", + "outputGlob": "dist/*/browser", + "port": 3000, + "installCommand": "npm install --legacy-peer-deps" + }, + "next.js": { + "tier": 2, + "displayName": "Next.js (App Router)", + "container": "next", + "htWrappers": ["@handsontable/react-wrapper"], + "entry": "app/page.tsx", + "devCommand": "next dev -p 3000 -H 0.0.0.0", + "buildCommand": "next build", + "outputDir": "out", + "staticExport": true, + "port": 3000, + "installCommand": "npm install" + }, + "next-shadcn.js": { + "tier": 2, + "displayName": "Next.js + shadcn/ui", + "container": "next", + "htWrappers": ["@handsontable/react-wrapper"], + "entry": "app/page.tsx", + "devCommand": "next dev -p 3000 -H 0.0.0.0", + "buildCommand": "next build", + "outputDir": "out", + "staticExport": true, + "port": 3000, + "installCommand": "npm install" + }, + "astro": { + "tier": 2, + "displayName": "Astro", + "container": "astro", + "htWrappers": [], + "entry": "src/pages/index.astro", + "devCommand": "astro dev --host 0.0.0.0 --port 4321", + "buildCommand": "astro build", + "outputDir": "dist", + "port": 4321, + "installCommand": "npm install" + }, + "nuxt": { + "tier": 2, + "displayName": "Nuxt 3", + "container": "nuxt", + "htWrappers": ["@handsontable/vue3"], + "entry": "app.vue", + "devCommand": "nuxt dev -H 0.0.0.0 -p 3000", + "buildCommand": "nuxt generate", + "outputDir": ".output/public", + "port": 3000, + "installCommand": "npm install" + }, + "remix": { + "tier": 2, + "displayName": "Remix", + "container": "remix", + "htWrappers": ["@handsontable/react-wrapper"], + "entry": "app/routes/_index.tsx", + "devCommand": "remix vite:dev --host --port 5173", + "buildCommand": "remix vite:build", + "outputDir": "build/client", + "spaMode": true, + "port": 5173, + "installCommand": "npm install" + } + } +} diff --git a/runner/docs/architecture.md b/runner/docs/architecture.md new file mode 100644 index 0000000..f06d7d1 --- /dev/null +++ b/runner/docs/architecture.md @@ -0,0 +1,140 @@ +# Architecture + +## Goal + +Replace CodeSandbox (too expensive) and the `render-ms` redirect microservice +with a self-hosted system that (a) renders every example live at any Handsontable +version, (b) lets internal team members edit code on the fly, and (c) produces a +clean, permanent client URL to share. + +## One UX, two engines behind an adapter + +The editor shell binds only to the `DemoRuntime` interface +(`packages/runtime/src/types.ts`). `resolveRuntime(entry)` selects the engine by +tier: + +``` +DemoRuntime + ├─ SandpackRuntime (Tier 1) — @codesandbox/sandpack-client, in-browser bundler + └─ ContainerRuntime (Tier 2) — WebSocket to a Cloudflare Sandbox session; + iframe = container preview URL +``` + +- **Tier 1 — client-side (free, fastest):** `example1`, `javascript`, + `typescript`, `react`, `react-js`, `ant-design`, `vue`. Bundles in the browser; + no server; tens-of-ms edit latency; zero compute cost. +- **Tier 2 — SSR / meta-framework:** `angular`, `next.js`, `next-shadcn.js`, + `astro`, `nuxt`, `remix`. A per-session container runs the framework's real + `npm run dev` with HMR; edits stream over WebSocket; the preview iframe points + at the container's preview URL. Auto-sleeps after inactivity. + +> Angular is Tier 2: the Angular versions used here need a real Node backend/dev +> server rather than Sandpack's in-browser Angular template. + +## Version dispatch + +`applyHandsontableVersion(files, version)` (`packages/runtime/src/version.ts`) +rewrites `package.json` so `handsontable` and its framework wrapper are pinned in +lockstep to the requested version — before mount (Tier 1) or build (Tier 2). +`@handsontable/pikaday` is never rewritten. Inputs accepted (ported from +`render-ms`): semver (incl. npm-style partials like `17.0`), a bare pkg.pr.new +numeric id, or a `https://pkg.pr.new/...` URL. Major capped at +`HANDSONTABLE_MAX_MAJOR` (default 19). Supported wrapper range: majors 15–19 +(pre-15 used different wrapper package names — out of scope). + +Per-framework wrapper map (pinned in lockstep with `handsontable`): + +| Framework(s) | Wrapper pinned | +|---|---| +| `example1`, `javascript`, `typescript`, `astro` | (core only) | +| `react`, `react-js`, `ant-design`, `next.js`, `next-shadcn.js`, `remix` | `@handsontable/react-wrapper` | +| `vue`, `nuxt` | `@handsontable/vue3` | +| `angular` | `@handsontable/angular-wrapper` | + +## Sharing model — cost stays bounded + +Client-facing shares are **prebuilt static, never live sessions**. On Share: +snapshot files → run the real framework build → upload to R2 → mint a short id → +return `https://demos.handsontable.com/d/:id`. Immutable, permanent, revocable +(410 when revoked). No container runs for client views. + +Static build modes per Tier-2 framework: + +| Framework | Build for share | Static output | +|---|---|---| +| `angular` | `ng build` | `dist/*/browser` | +| `next.js`, `next-shadcn.js` | `next build` (`output: 'export'`) | `out/` | +| `astro` | `astro build` | `dist/` | +| `nuxt` | `nuxt generate` | `.output/public/` | +| `remix` | `remix vite:build` (SPA mode, `ssr: false`) | `build/client/` | + +A built artifact is immutable per `(framework, ht_version, files_hash)` — cached +forever. + +## Storage + +- **D1** — demo metadata + short ids (see schema below). +- **R2** — source snapshots + built artifacts. +- **KV** — edge read-cache for demo JSON (`stale-while-revalidate`). + +### D1 schema + +```sql +CREATE TABLE demos ( + id TEXT PRIMARY KEY, + title TEXT NOT NULL, + framework TEXT NOT NULL, + tier INTEGER NOT NULL, + ht_version TEXT NOT NULL, + files_hash TEXT NOT NULL, + r2_prefix TEXT NOT NULL, + visibility TEXT NOT NULL DEFAULT 'unlisted', + revoked INTEGER NOT NULL DEFAULT 0, + created_by TEXT NOT NULL, + created_at TEXT NOT NULL, + updated_at TEXT NOT NULL, + revoked_at TEXT +); +CREATE INDEX idx_demos_framework ON demos(framework); +CREATE INDEX idx_demos_created_by ON demos(created_by); +CREATE UNIQUE INDEX idx_demos_buildkey ON demos(framework, ht_version, files_hash); + +CREATE TABLE build_cache ( + build_key TEXT PRIMARY KEY, + r2_prefix TEXT NOT NULL, + created_at TEXT NOT NULL +); +``` + +## Auth & domains + +Cloudflare Access gates `apps/authoring` and all write endpoints. The viewer and +`GET /api/demos/:id` are public. Authoring lives on an Access-gated subdomain; +public shares at `demos.handsontable.com/d/:id`. + +## Hard constraints + +No Nodebox / WebContainers (Tier 2 runs the real open-source frameworks in our +own Cloudflare containers). White-label — no CodeSandbox marks in the UI. Secrets +in environment variables only. No per-view compute cost. + +## Relationship to `render-ms` + +`render-ms` keeps running until this system fully replaces it. This PR adds a +compatibility shim mapping old `render-ms` deep links +(`example-dir`, `handsontable-version`, `handsontable-branch`, +`handsontable-sha`, `example-branch`) to the new viewer; `render-ms` removal is a +follow-up PR. + +## Deliverables + +1. ✅ Monorepo scaffold + catalog importer + migration of all 13 examples. +2. `packages/runtime`: `SandpackRuntime` (Tier 1) for all 7 client-side frameworks. +3. `packages/editor-shell` + `apps/authoring`: unified editor behind Access. +4. Tier 2: `containers/*` + `ContainerRuntime` + Sandbox SDK orchestration. +5. `workers/api` sharing endpoints + D1/R2/KV. +6. `apps/viewer` at `/d/:id` + opt-in "Edit live". +7. `pipeline/` build-and-serve snapshotter + version injection + `scripts/warm.ts`. +8. `render-ms` compatibility shim. +9. `docs/`: run/deploy, self-host-bundler, non-technical share guide. +``` diff --git a/runner/package.json b/runner/package.json new file mode 100644 index 0000000..623e5bd --- /dev/null +++ b/runner/package.json @@ -0,0 +1,21 @@ +{ + "name": "@handsontable/demo-runner", + "private": true, + "version": "0.0.0", + "type": "module", + "description": "Self-hosted live demo runner for Handsontable examples (replaces CodeSandbox / render-ms).", + "license": "UNLICENSED", + "packageManager": "pnpm@10.12.2", + "engines": { + "node": ">=22" + }, + "scripts": { + "import": "node pipeline/import.mjs", + "build": "pnpm -r --filter \"./packages/**\" run build", + "typecheck": "pnpm -r run typecheck", + "lint": "pnpm -r run lint" + }, + "devDependencies": { + "typescript": "~5.6.0" + } +} diff --git a/runner/packages/editor-shell/package.json b/runner/packages/editor-shell/package.json new file mode 100644 index 0000000..c351c79 --- /dev/null +++ b/runner/packages/editor-shell/package.json @@ -0,0 +1,13 @@ +{ + "name": "@handsontable/demo-editor-shell", + "version": "0.0.0", + "private": true, + "type": "module", + "main": "./src/index.ts", + "scripts": { + "typecheck": "tsc -p tsconfig.json --noEmit" + }, + "dependencies": { + "@handsontable/demo-runtime": "workspace:*" + } +} diff --git a/runner/packages/editor-shell/src/index.ts b/runner/packages/editor-shell/src/index.ts new file mode 100644 index 0000000..b510e95 --- /dev/null +++ b/runner/packages/editor-shell/src/index.ts @@ -0,0 +1,5 @@ +// Framework-agnostic editor shell. The full UI (file tree, code editor, preview +// slot, toolbar, version picker, Share) lands in Deliverable 3; for now the +// package exposes the branding tokens the apps consume. +export { theme } from "./theme.js"; +export type { Theme } from "./theme.js"; diff --git a/runner/packages/editor-shell/src/theme.ts b/runner/packages/editor-shell/src/theme.ts new file mode 100644 index 0000000..32a9807 --- /dev/null +++ b/runner/packages/editor-shell/src/theme.ts @@ -0,0 +1,43 @@ +// Single source of branding tokens for the authoring shell and the public viewer. +// White-label: our tokens only — no CodeSandbox marks anywhere in the UI. +// (Sandpack is Apache-2.0; its license notice lives in source, never in the UI.) +// Refined against the Handsontable brand guide during the design pass. + +export const theme = { + color: { + accent: "#1a8f5a", // Handsontable green + accentHover: "#157a4c", + accentContrast: "#ffffff", + + surface: "#ffffff", + surfaceMuted: "#f4f6f8", + border: "#e2e6ea", + text: "#1f2933", + textMuted: "#647382", + + // Editor / code surfaces (dark). + editorBg: "#0f1720", + editorGutter: "#0b121a", + editorText: "#e6edf3", + editorSelection: "#1f6feb44", + + // Syntax (aligned to the accent family; tuned in the design pass). + synKeyword: "#7ee787", + synString: "#a5d6ff", + synNumber: "#f2cc60", + synComment: "#8b949e", + synFunction: "#d2a8ff", + synVariable: "#e6edf3", + + danger: "#d1242f", + warning: "#bf8700", + }, + font: { + ui: `-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif`, + mono: `"JetBrains Mono", "SFMono-Regular", ui-monospace, Menlo, Consolas, monospace`, + }, + radius: { sm: "4px", md: "8px", lg: "12px" }, + space: (n: number) => `${n * 4}px`, +} as const; + +export type Theme = typeof theme; diff --git a/runner/packages/editor-shell/tsconfig.json b/runner/packages/editor-shell/tsconfig.json new file mode 100644 index 0000000..3664f57 --- /dev/null +++ b/runner/packages/editor-shell/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "noEmit": true, + "jsx": "react-jsx" + }, + "include": ["src/**/*"] +} diff --git a/runner/packages/runtime/package.json b/runner/packages/runtime/package.json new file mode 100644 index 0000000..8c2ec22 --- /dev/null +++ b/runner/packages/runtime/package.json @@ -0,0 +1,24 @@ +{ + "name": "@handsontable/demo-runtime", + "version": "0.0.0", + "private": true, + "type": "module", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + } + }, + "scripts": { + "build": "tsc -p tsconfig.json", + "typecheck": "tsc -p tsconfig.json --noEmit" + }, + "dependencies": { + "semver": "^7.7.4" + }, + "devDependencies": { + "@types/semver": "^7.5.8" + } +} diff --git a/runner/packages/runtime/src/index.ts b/runner/packages/runtime/src/index.ts new file mode 100644 index 0000000..2fe5a3f --- /dev/null +++ b/runner/packages/runtime/src/index.ts @@ -0,0 +1,24 @@ +export type { + FilesMap, + Tier, + CatalogEntry, + Catalog, + DemoRuntime, + HandsontableVersionRef, +} from "./types.js"; + +export { + applyHandsontableVersion, + validateHandsontableVersion, + isHandsontablePackage, + pkgPrNewDependencyUrl, + parsePkgPrNewFromUrl, + DEFAULT_MAX_MAJOR, +} from "./version.js"; +export type { ValidationResult } from "./version.js"; + +export { + resolveRuntime, + resolveRuntimeKind, +} from "./resolveRuntime.js"; +export type { RuntimeKind, RuntimeFactories } from "./resolveRuntime.js"; diff --git a/runner/packages/runtime/src/resolveRuntime.ts b/runner/packages/runtime/src/resolveRuntime.ts new file mode 100644 index 0000000..62d610d --- /dev/null +++ b/runner/packages/runtime/src/resolveRuntime.ts @@ -0,0 +1,26 @@ +// resolveRuntime — picks the engine for a catalog entry. Tier 1 -> Sandpack +// (in-browser bundler); Tier 2 -> Cloudflare Sandbox container. The concrete +// SandpackRuntime and ContainerRuntime are implemented in later deliverables +// (2 and 4); this module only decides which one to construct so the shell can +// stay engine-agnostic. + +import type { CatalogEntry, DemoRuntime } from "./types.js"; + +export type RuntimeKind = "sandpack" | "container"; + +export function resolveRuntimeKind(entry: Pick): RuntimeKind { + return entry.tier === 1 ? "sandpack" : "container"; +} + +export interface RuntimeFactories { + sandpack: (entry: CatalogEntry) => DemoRuntime; + container: (entry: CatalogEntry) => DemoRuntime; +} + +/** + * Construct the runtime for an entry using injected factories. Apps wire the two + * concrete factories once; callers then just pass a catalog entry. + */ +export function resolveRuntime(entry: CatalogEntry, factories: RuntimeFactories): DemoRuntime { + return factories[resolveRuntimeKind(entry)](entry); +} diff --git a/runner/packages/runtime/src/types.ts b/runner/packages/runtime/src/types.ts new file mode 100644 index 0000000..b4657ec --- /dev/null +++ b/runner/packages/runtime/src/types.ts @@ -0,0 +1,61 @@ +// Core types shared by every part of the system. The editor shell binds only to +// `DemoRuntime`; whether a Sandpack in-browser bundler or a Cloudflare Sandbox +// container is behind it is an invisible implementation detail. + +/** Virtual filesystem: absolute-from-root path (e.g. "/src/index.tsx") -> file contents. */ +export type FilesMap = Record; + +export type Tier = 1 | 2; + +/** One importable starting template, as emitted into catalog.json by pipeline/import. */ +export interface CatalogEntry { + framework: string; + displayName: string; + tier: Tier; + sandpackTemplate: string | null; + container: string | null; + htWrappers: string[]; + entry: string; + htmlEntry: string | null; + devCommand: string | null; + buildCommand: string; + outputDir: string; + outputGlob: string | null; + staticExport: boolean; + spaMode: boolean; + port: number | null; + installCommand: string; + htCoreRange: string | null; + fileCount: number; + assets: string[]; + skipped: { path: string; reason: string }[]; + files: FilesMap; +} + +export interface Catalog { + generatedFrom: string; + supportedHandsontableMajors: number[]; + tiers: Record; + examples: CatalogEntry[]; +} + +/** + * The single interface both engines implement. `mount` boots the preview and + * returns the URL to point the iframe at; `writeFile` streams edits (on save or + * keystroke); lifecycle callbacks drive the shell's ready/error UI. + */ +export interface DemoRuntime { + mount(files: FilesMap): Promise<{ previewUrl: string }>; + writeFile(path: string, contents: string): void; + onReady(cb: () => void): void; + onError(cb: (e: Error) => void): void; + dispose(): void; +} + +/** A resolved, validated Handsontable version reference. */ +export interface HandsontableVersionRef { + /** Either a semver string ("18.0.0") or a pkg.pr.new build ref. */ + ref: string; + /** True when `ref` is a pkg.pr.new build id/URL rather than a published semver. */ + pkgPrNew: boolean; +} diff --git a/runner/packages/runtime/src/version.ts b/runner/packages/runtime/src/version.ts new file mode 100644 index 0000000..a93e2e7 --- /dev/null +++ b/runner/packages/runtime/src/version.ts @@ -0,0 +1,147 @@ +// Version dispatch — ported from render-ms (validate-handsontable-version.js, +// pkg-pr-new.js) and its package.json dependency rewrite, so the new system +// accepts exactly the same `handsontable-version` inputs the old deep links used. +// +// Rule (identical to render-ms): rewrite every dependency whose package name +// contains "handsontable" EXCEPT `@handsontable/pikaday` to the requested +// version (or a pkg.pr.new URL). This pins core `handsontable` and its framework +// wrapper in lockstep. Supported Handsontable major range: 15-19. + +import semver from "semver"; +import type { FilesMap, HandsontableVersionRef } from "./types.js"; + +export const DEFAULT_MAX_MAJOR = 19; +const MIN_BARE_NUMERIC_PKG_PR_NEW_REF = 1000; + +/** Dependency never rewritten: an independently versioned Handsontable plugin. */ +const NEVER_REWRITE = new Set(["@handsontable/pikaday"]); + +export function pkgPrNewDependencyUrl(packageName: string, buildRef: string): string { + return `https://pkg.pr.new/${packageName}@${buildRef}`; +} + +/** Parse a full https://pkg.pr.new/...@ref URL; bare numeric ids handled in validation. */ +export function parsePkgPrNewFromUrl(value: string): string | null { + try { + const u = new URL(String(value).trim()); + if (u.protocol !== "https:" || u.hostname !== "pkg.pr.new") return null; + const p = u.pathname.replace(/^\//, ""); + const at = p.lastIndexOf("@"); + if (at <= 0) return null; + const ref = p.slice(at + 1); + return ref === "" ? null : ref; + } catch { + return null; + } +} + +function expandPartialNumericSemver(s: string): string | null { + if (!/^\d+(?:\.\d+)+$/.test(s)) return null; + const parts = s.split("."); + if (parts.length > 3 || parts.some((p) => !/^\d+$/.test(p))) return null; + const [major, minor = 0, patch = 0] = parts.map((p) => Number.parseInt(p, 10)); + if ([major, minor, patch].some((n) => !Number.isFinite(n) || n < 0)) return null; + return `${major}.${minor}.${patch}`; +} + +export type ValidationResult = + | { ok: true; value: HandsontableVersionRef } + | { ok: false; message: string }; + +/** + * Validate a `handsontable-version` input: semver (loose, npm-style partials like + * "17.0"), a bare pkg.pr.new numeric id, or a https://pkg.pr.new/... URL. Enforces + * major <= maxMajor for real semver. + */ +export function validateHandsontableVersion( + value: unknown, + maxMajor: number = DEFAULT_MAX_MAJOR, +): ValidationResult { + if (value === undefined || value === null) { + return { ok: false, message: "handsontable-version is required" }; + } + const trimmed = String(value).trim(); + if (trimmed === "") return { ok: false, message: "handsontable-version cannot be empty" }; + + const urlRef = parsePkgPrNewFromUrl(trimmed); + if (urlRef !== null) return { ok: true, value: { ref: urlRef, pkgPrNew: true } }; + + const capMsg = (normalized: string) => { + const major = semver.major(normalized); + return major > maxMajor + ? `handsontable-version major must be at most ${maxMajor}; got ${major}` + : null; + }; + + if (/^\d+$/.test(trimmed)) { + const n = Number.parseInt(trimmed, 10); + if (!Number.isSafeInteger(n) || n < 0) { + return { ok: false, message: `handsontable-version is not a valid numeric id` }; + } + if (n > maxMajor) { + if (n < MIN_BARE_NUMERIC_PKG_PR_NEW_REF) { + return { ok: false, message: `handsontable-version major must be at most ${maxMajor}` }; + } + return { ok: true, value: { ref: trimmed, pkgPrNew: true } }; + } + const coerced = semver.coerce(trimmed); + const normalized = coerced ? semver.valid(coerced.version, { loose: true }) : null; + if (!normalized) return { ok: false, message: `handsontable-version could not be interpreted as semver` }; + const err = capMsg(normalized); + return err ? { ok: false, message: err } : { ok: true, value: { ref: normalized, pkgPrNew: false } }; + } + + let normalized = semver.valid(trimmed, { loose: true }); + if (!normalized) { + const expanded = expandPartialNumericSemver(trimmed); + if (expanded) normalized = semver.valid(expanded, { loose: true }); + } + if (!normalized) { + return { ok: false, message: `handsontable-version must be semver-valid or a pkg.pr.new id/URL` }; + } + const err = capMsg(normalized); + return err ? { ok: false, message: err } : { ok: true, value: { ref: normalized, pkgPrNew: false } }; +} + +/** True if `name` is a Handsontable package that should be version-pinned. */ +export function isHandsontablePackage(name: string): boolean { + return name.includes("handsontable") && !NEVER_REWRITE.has(name); +} + +/** + * Return a new FilesMap with package.json's Handsontable deps pinned to `version`. + * Pure: does not mutate the input. Throws if package.json is missing/unparseable. + */ +export function applyHandsontableVersion( + files: FilesMap, + version: HandsontableVersionRef, + pkgPath = "/package.json", +): FilesMap { + const raw = files[pkgPath]; + if (raw === undefined) throw new Error(`applyHandsontableVersion: ${pkgPath} not found`); + + let pkg: { dependencies?: Record; [k: string]: unknown }; + try { + pkg = JSON.parse(raw); + } catch (e) { + throw new Error(`applyHandsontableVersion: ${pkgPath} is not valid JSON: ${(e as Error).message}`); + } + + const rewrite = (deps: Record | undefined) => { + if (!deps) return deps; + return Object.fromEntries( + Object.entries(deps).map(([name, range]) => + isHandsontablePackage(name) + ? [name, version.pkgPrNew ? pkgPrNewDependencyUrl(name, version.ref) : version.ref] + : [name, range], + ), + ); + }; + + const next = { + ...pkg, + dependencies: rewrite(pkg.dependencies), + }; + + return { ...files, [pkgPath]: JSON.stringify(next, null, 2) + "\n" }; +} diff --git a/runner/packages/runtime/tsconfig.json b/runner/packages/runtime/tsconfig.json new file mode 100644 index 0000000..8f24167 --- /dev/null +++ b/runner/packages/runtime/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./dist", + "rootDir": "./src" + }, + "include": ["src/**/*"] +} diff --git a/runner/pipeline/import.mjs b/runner/pipeline/import.mjs new file mode 100644 index 0000000..f2bb1fb --- /dev/null +++ b/runner/pipeline/import.mjs @@ -0,0 +1,204 @@ +// pipeline/import.mjs +// +// Reads the 13 example directories from the source repo's `examples/` folder and +// emits `runner/catalog.json` — the starting-template catalog the authoring UI +// browses. Each entry is normalized to { framework, tier, files, entry, +// devCommand, buildCommand, ... } sourced from config/frameworks.json. +// +// Run: node runner/pipeline/import.mjs (from repo root) +// or: node import.mjs (from runner/pipeline) +// +// Deterministic and dependency-free (Node built-ins only). Build artifacts, +// lockfiles, node_modules, CodeSandbox metadata, and binary assets are excluded; +// binary assets are recorded (path only) so a later stage can copy them verbatim. + +import fs from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const RUNNER_DIR = path.resolve(__dirname, ".."); +const REPO_ROOT = path.resolve(RUNNER_DIR, ".."); +const EXAMPLES_DIR = path.join(REPO_ROOT, "examples"); +const CONFIG = JSON.parse( + fs.readFileSync(path.join(RUNNER_DIR, "config", "frameworks.json"), "utf8"), +); +const FRAMEWORKS = CONFIG.frameworks; + +// Directories never copied into a template. +const EXCLUDE_DIRS = new Set([ + "node_modules", + ".git", + ".cache", + "dist", + "build", + ".next", + ".nuxt", + ".output", + ".astro", + ".codesandbox", // CodeSandbox-specific config; we are replacing CodeSandbox + ".devcontainer", + ".vscode", + ".changelog-prs-cache", +]); + +// Files never copied: lockfiles are regenerated after version injection. +const EXCLUDE_FILES = new Set([ + "package-lock.json", + "pnpm-lock.yaml", + "yarn.lock", + ".DS_Store", +]); + +// Extensions treated as binary assets (recorded, not inlined as text). +const BINARY_EXT = new Set([ + ".ico", ".png", ".jpg", ".jpeg", ".gif", ".webp", ".avif", + ".woff", ".woff2", ".ttf", ".eot", ".otf", + ".mp4", ".webm", ".mp3", ".wav", ".pdf", ".zip", +]); + +const MAX_TEXT_BYTES = 512 * 1024; // skip pathologically large text files + +function walk(dir, base = dir, acc = []) { + for (const name of fs.readdirSync(dir)) { + const full = path.join(dir, name); + const rel = path.relative(base, full).split(path.sep).join("/"); + const stat = fs.statSync(full); + if (stat.isDirectory()) { + if (EXCLUDE_DIRS.has(name)) continue; + walk(full, base, acc); + } else if (stat.isFile()) { + if (EXCLUDE_FILES.has(name)) continue; + acc.push({ rel, full, size: stat.size }); + } + } + return acc; +} + +function looksBinary(buf) { + // Heuristic: a NUL byte in the first 8 KB → binary. + const n = Math.min(buf.length, 8192); + for (let i = 0; i < n; i++) if (buf[i] === 0) return true; + return false; +} + +function normalizeEol(text) { + return text.replace(/\r\n/g, "\n"); +} + +function importExample(framework) { + const cfg = FRAMEWORKS[framework]; + const dir = path.join(EXAMPLES_DIR, framework); + if (!fs.existsSync(dir)) { + throw new Error(`example directory not found: ${dir}`); + } + + const files = {}; + const assets = []; + const skipped = []; + + for (const { rel, full, size } of walk(dir)) { + const ext = path.extname(rel).toLowerCase(); + if (BINARY_EXT.has(ext)) { + assets.push(rel); + continue; + } + if (size > MAX_TEXT_BYTES) { + skipped.push({ path: rel, reason: `>${MAX_TEXT_BYTES}B` }); + continue; + } + const buf = fs.readFileSync(full); + if (looksBinary(buf)) { + assets.push(rel); + continue; + } + files[`/${rel}`] = normalizeEol(buf.toString("utf8")); + } + + const pkgKey = "/package.json"; + let htCoreRange = null; + if (files[pkgKey]) { + try { + const deps = JSON.parse(files[pkgKey]).dependencies || {}; + htCoreRange = deps.handsontable ?? null; + } catch { + /* leave null; surfaced via fileCount sanity check below */ + } + } + + return { + framework, + displayName: cfg.displayName, + tier: cfg.tier, + sandpackTemplate: cfg.sandpackTemplate ?? null, + container: cfg.container ?? null, + htWrappers: cfg.htWrappers, + entry: `/${cfg.entry}`, + htmlEntry: cfg.htmlEntry ? `/${cfg.htmlEntry}` : null, + devCommand: cfg.devCommand, + buildCommand: cfg.buildCommand, + outputDir: cfg.outputDir, + outputGlob: cfg.outputGlob ?? null, + staticExport: cfg.staticExport ?? false, + spaMode: cfg.spaMode ?? false, + port: cfg.port, + installCommand: cfg.installCommand, + htCoreRange, + fileCount: Object.keys(files).length, + assets, + skipped, + files, + }; +} + +function main() { + const frameworks = Object.keys(FRAMEWORKS); + const examples = frameworks.map(importExample); + + // Sanity: every example must have package.json and its declared entry file. + const problems = []; + for (const ex of examples) { + if (!ex.files["/package.json"]) problems.push(`${ex.framework}: missing package.json`); + if (!ex.files[ex.entry] && !(ex.assets || []).includes(ex.entry.slice(1))) { + problems.push(`${ex.framework}: declared entry ${ex.entry} not found among files`); + } + if (ex.fileCount === 0) problems.push(`${ex.framework}: no files imported`); + } + if (problems.length) { + console.error("[import] catalog validation problems:\n - " + problems.join("\n - ")); + process.exitCode = 1; + } + + const catalog = { + generatedFrom: "handsontable/examples examples/", + supportedHandsontableMajors: [15, 16, 17, 18, 19], + tiers: { + "1": "client-side (Sandpack, in-browser bundler)", + "2": "SSR/meta-framework (Cloudflare Sandbox container)", + }, + examples, + }; + + const out = path.join(RUNNER_DIR, "catalog.json"); + fs.writeFileSync(out, JSON.stringify(catalog, null, 2) + "\n"); + + // Human-readable summary to stdout. + const t1 = examples.filter((e) => e.tier === 1).map((e) => e.framework); + const t2 = examples.filter((e) => e.tier === 2).map((e) => e.framework); + console.log(`[import] wrote ${path.relative(REPO_ROOT, out)}`); + console.log(`[import] ${examples.length} examples imported`); + console.log(`[import] Tier 1 (${t1.length}): ${t1.join(", ")}`); + console.log(`[import] Tier 2 (${t2.length}): ${t2.join(", ")}`); + console.table( + examples.map((e) => ({ + framework: e.framework, + tier: e.tier, + files: e.fileCount, + assets: e.assets.length, + htCore: e.htCoreRange, + entry: e.entry, + })), + ); +} + +main(); diff --git a/runner/pnpm-workspace.yaml b/runner/pnpm-workspace.yaml new file mode 100644 index 0000000..ec62634 --- /dev/null +++ b/runner/pnpm-workspace.yaml @@ -0,0 +1,5 @@ +packages: + - "packages/*" + - "apps/*" + - "workers/*" + - "pipeline" diff --git a/runner/tsconfig.base.json b/runner/tsconfig.base.json new file mode 100644 index 0000000..523638b --- /dev/null +++ b/runner/tsconfig.base.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "Bundler", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "strict": true, + "noUncheckedIndexedAccess": true, + "esModuleInterop": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "declaration": true, + "sourceMap": true, + "verbatimModuleSyntax": true, + "isolatedModules": true + } +} From b47ffa26936ecf4d5d9a7c526517c8c2617bb79d Mon Sep 17 00:00:00 2001 From: Mateusz Wojczal Date: Wed, 8 Jul 2026 10:56:19 +0200 Subject: [PATCH 02/29] runner: SandpackRuntime (Tier-1) + version dispatch for all 7 client-side frameworks Deliverable 2. - packages/runtime/src/sandpack.ts: SandpackRuntime implementing DemoRuntime over @codesandbox/sandpack-client (classic in-browser bundler). White-label (showOpenInCodeSandbox:false); Sandpack option/setup types derived from the loader signature; exported via the ./sandpack subpath so DOM code never reaches the sharing Worker. Applies applyHandsontableVersion before mount; writeFile streams edits via updateSandbox. - config/frameworks.json + catalog: sandpackEnvironment per Tier-1 example (parcel / create-react-app[-typescript] / vue-cli). - version.ts: fix expandPartialNumericSemver under noUncheckedIndexedAccess. - Verified from compiled dist: version validation (semver, npm partials, major cap, pkg.pr.new id/URL) and lockstep pinning are correct and pure. --- runner/catalog.json | 13 ++ runner/config/frameworks.json | 7 + runner/packages/runtime/package.json | 5 + runner/packages/runtime/src/sandpack.ts | 168 ++++++++++++++++++++++++ runner/packages/runtime/src/types.ts | 2 + runner/packages/runtime/src/version.ts | 7 +- runner/pipeline/import.mjs | 1 + runner/pnpm-lock.yaml | 136 +++++++++++++++++++ 8 files changed, 337 insertions(+), 2 deletions(-) create mode 100644 runner/packages/runtime/src/sandpack.ts create mode 100644 runner/pnpm-lock.yaml diff --git a/runner/catalog.json b/runner/catalog.json index f43d865..5aba616 100644 --- a/runner/catalog.json +++ b/runner/catalog.json @@ -17,6 +17,7 @@ "displayName": "Vanilla (Parcel)", "tier": 1, "sandpackTemplate": "vanilla-ts", + "sandpackEnvironment": "parcel", "container": null, "htWrappers": [], "entry": "/index.ts", @@ -45,6 +46,7 @@ "displayName": "JavaScript (Vite)", "tier": 1, "sandpackTemplate": "vanilla", + "sandpackEnvironment": "parcel", "container": null, "htWrappers": [], "entry": "/index.js", @@ -79,6 +81,7 @@ "displayName": "TypeScript (Vite)", "tier": 1, "sandpackTemplate": "vanilla-ts", + "sandpackEnvironment": "parcel", "container": null, "htWrappers": [], "entry": "/index.ts", @@ -111,6 +114,7 @@ "displayName": "React (Vite, TS)", "tier": 1, "sandpackTemplate": "react-ts", + "sandpackEnvironment": "create-react-app-typescript", "container": null, "htWrappers": [ "@handsontable/react-wrapper" @@ -152,6 +156,7 @@ "displayName": "React (Vite, JS)", "tier": 1, "sandpackTemplate": "react", + "sandpackEnvironment": "create-react-app", "container": null, "htWrappers": [ "@handsontable/react-wrapper" @@ -188,6 +193,7 @@ "displayName": "Ant Design + React", "tier": 1, "sandpackTemplate": "react", + "sandpackEnvironment": "create-react-app", "container": null, "htWrappers": [ "@handsontable/react-wrapper" @@ -220,6 +226,7 @@ "displayName": "Vue 3 (Vite)", "tier": 1, "sandpackTemplate": "vue-ts", + "sandpackEnvironment": "vue-cli", "container": null, "htWrappers": [ "@handsontable/vue3" @@ -274,6 +281,7 @@ "displayName": "Angular", "tier": 2, "sandpackTemplate": null, + "sandpackEnvironment": null, "container": "angular", "htWrappers": [ "@handsontable/angular-wrapper" @@ -319,6 +327,7 @@ "displayName": "Next.js (App Router)", "tier": 2, "sandpackTemplate": null, + "sandpackEnvironment": null, "container": "next", "htWrappers": [ "@handsontable/react-wrapper" @@ -363,6 +372,7 @@ "displayName": "Next.js + shadcn/ui", "tier": 2, "sandpackTemplate": null, + "sandpackEnvironment": null, "container": "next", "htWrappers": [ "@handsontable/react-wrapper" @@ -410,6 +420,7 @@ "displayName": "Astro", "tier": 2, "sandpackTemplate": null, + "sandpackEnvironment": null, "container": "astro", "htWrappers": [], "entry": "/src/pages/index.astro", @@ -445,6 +456,7 @@ "displayName": "Nuxt 3", "tier": 2, "sandpackTemplate": null, + "sandpackEnvironment": null, "container": "nuxt", "htWrappers": [ "@handsontable/vue3" @@ -483,6 +495,7 @@ "displayName": "Remix", "tier": 2, "sandpackTemplate": null, + "sandpackEnvironment": null, "container": "remix", "htWrappers": [ "@handsontable/react-wrapper" diff --git a/runner/config/frameworks.json b/runner/config/frameworks.json index 0c6a7d1..915a304 100644 --- a/runner/config/frameworks.json +++ b/runner/config/frameworks.json @@ -6,6 +6,7 @@ "tier": 1, "displayName": "Vanilla (Parcel)", "sandpackTemplate": "vanilla-ts", + "sandpackEnvironment": "parcel", "htWrappers": [], "entry": "index.ts", "htmlEntry": "index.html", @@ -19,6 +20,7 @@ "tier": 1, "displayName": "JavaScript (Vite)", "sandpackTemplate": "vanilla", + "sandpackEnvironment": "parcel", "htWrappers": [], "entry": "index.js", "htmlEntry": "index.html", @@ -32,6 +34,7 @@ "tier": 1, "displayName": "TypeScript (Vite)", "sandpackTemplate": "vanilla-ts", + "sandpackEnvironment": "parcel", "htWrappers": [], "entry": "index.ts", "htmlEntry": "index.html", @@ -45,6 +48,7 @@ "tier": 1, "displayName": "React (Vite, TS)", "sandpackTemplate": "react-ts", + "sandpackEnvironment": "create-react-app-typescript", "htWrappers": ["@handsontable/react-wrapper"], "entry": "src/index.tsx", "htmlEntry": "index.html", @@ -58,6 +62,7 @@ "tier": 1, "displayName": "React (Vite, JS)", "sandpackTemplate": "react", + "sandpackEnvironment": "create-react-app", "htWrappers": ["@handsontable/react-wrapper"], "entry": "index.jsx", "htmlEntry": "index.html", @@ -71,6 +76,7 @@ "tier": 1, "displayName": "Ant Design + React", "sandpackTemplate": "react", + "sandpackEnvironment": "create-react-app", "htWrappers": ["@handsontable/react-wrapper"], "entry": "src/index.jsx", "htmlEntry": "index.html", @@ -84,6 +90,7 @@ "tier": 1, "displayName": "Vue 3 (Vite)", "sandpackTemplate": "vue-ts", + "sandpackEnvironment": "vue-cli", "htWrappers": ["@handsontable/vue3"], "entry": "src/main.ts", "htmlEntry": "index.html", diff --git a/runner/packages/runtime/package.json b/runner/packages/runtime/package.json index 8c2ec22..e2e3210 100644 --- a/runner/packages/runtime/package.json +++ b/runner/packages/runtime/package.json @@ -9,6 +9,10 @@ ".": { "types": "./dist/index.d.ts", "default": "./dist/index.js" + }, + "./sandpack": { + "types": "./dist/sandpack.d.ts", + "default": "./dist/sandpack.js" } }, "scripts": { @@ -16,6 +20,7 @@ "typecheck": "tsc -p tsconfig.json --noEmit" }, "dependencies": { + "@codesandbox/sandpack-client": "^2.19.8", "semver": "^7.7.4" }, "devDependencies": { diff --git a/runner/packages/runtime/src/sandpack.ts b/runner/packages/runtime/src/sandpack.ts new file mode 100644 index 0000000..1895a51 --- /dev/null +++ b/runner/packages/runtime/src/sandpack.ts @@ -0,0 +1,168 @@ +// SandpackRuntime — Tier-1 engine. Wraps @codesandbox/sandpack-client (the +// classic, client-side, in-browser bundler) behind the DemoRuntime interface. +// No server, no per-view cost. Phase 1 targets Sandpack's hosted bundler; set +// `bundlerURL` to a self-hosted deployment for Phase 2 (docs/self-host-bundler.md). +// +// White-label: showOpenInCodeSandbox is disabled and no CodeSandbox marks are +// surfaced. Sandpack is Apache-2.0 — license notice stays in source only. +// +// DOM-only: imported via the "@handsontable/demo-runtime/sandpack" subpath so it +// is never bundled into the (non-DOM) sharing Worker. + +import { loadSandpackClient } from "@codesandbox/sandpack-client"; +import type { CatalogEntry, DemoRuntime, FilesMap, HandsontableVersionRef } from "./types.js"; +import { applyHandsontableVersion } from "./version.js"; + +// Derive Sandpack's option/setup types straight from the loader signature so we +// don't depend on the package's exported type names staying stable. +type SandpackClientInstance = Awaited>; +type SandboxSetup = Parameters[1]; +type ClientOptions = Parameters[2]; + +export interface SandpackRuntimeOptions { + /** The shell's preview-iframe slot. Sandpack renders the preview into it. */ + iframe: HTMLIFrameElement; + /** Phase-1 hosted bundler by default; set to a self-hosted URL for Phase 2. */ + bundlerURL?: string; + /** Pin Handsontable to this version before mounting. */ + version?: HandsontableVersionRef; +} + +/** Environments whose entry point is the HTML file rather than a JS/TS module. */ +const HTML_ENTRY_ENVS = new Set(["parcel", "static"]); + +export class SandpackRuntime implements DemoRuntime { + private readonly entry: CatalogEntry; + private readonly opts: SandpackRuntimeOptions; + private client: SandpackClientInstance | null = null; + private files: FilesMap = {}; + private readonly readyCbs = new Set<() => void>(); + private readonly errorCbs = new Set<(e: Error) => void>(); + private unlisten: (() => void) | null = null; + private didReady = false; + + constructor(entry: CatalogEntry, opts: SandpackRuntimeOptions) { + if (entry.tier !== 1) { + throw new Error(`SandpackRuntime is Tier 1 only; ${entry.framework} is Tier ${entry.tier}`); + } + this.entry = entry; + this.opts = opts; + } + + onReady(cb: () => void): void { + this.readyCbs.add(cb); + if (this.didReady) cb(); + } + + onError(cb: (e: Error) => void): void { + this.errorCbs.add(cb); + } + + private emitReady() { + if (this.didReady) return; + this.didReady = true; + for (const cb of this.readyCbs) cb(); + } + + private emitError(e: Error) { + for (const cb of this.errorCbs) cb(e); + } + + /** Apply version dispatch, then shape files into a Sandpack sandbox setup. */ + private buildSetup(files: FilesMap): SandboxSetup { + const pinned = this.opts.version + ? applyHandsontableVersion(files, this.opts.version) + : files; + this.files = pinned; + + const sandpackFiles: Record = {}; + for (const [path, code] of Object.entries(pinned)) { + sandpackFiles[path] = { code }; + } + + const env = this.entry.sandpackEnvironment ?? undefined; + const entryPath = + env && HTML_ENTRY_ENVS.has(env) && this.entry.htmlEntry + ? this.entry.htmlEntry + : this.entry.entry; + + return { + files: sandpackFiles, + entry: entryPath, + // Dependencies are read from the injected /package.json by the bundler. + template: env, + } as SandboxSetup; + } + + private clientOptions(): ClientOptions { + const options: Record = { + showOpenInCodeSandbox: false, + showErrorScreen: true, + showLoadingScreen: true, + }; + if (this.opts.bundlerURL) options.bundlerURL = this.opts.bundlerURL; + return options as ClientOptions; + } + + async mount(files: FilesMap): Promise<{ previewUrl: string }> { + const setup = this.buildSetup(files); + this.client = await loadSandpackClient(this.opts.iframe, setup, this.clientOptions()); + + this.unlisten = this.client.listen((msg: unknown) => this.onMessage(msg)); + return { previewUrl: this.opts.iframe.src }; + } + + private onMessage(msg: unknown) { + const m = msg as { type?: string; action?: string; compilatonError?: boolean; message?: string }; + switch (m.type) { + case "done": + if (m.compilatonError) return; // error surfaced via its own message + this.emitReady(); + break; + case "action": + if (m.action === "show-error") { + this.emitError(new Error(m.message || "Sandpack compile error")); + } + break; + case "console": + break; + default: + break; + } + } + + /** Stream a single edit. Sandpack recompiles incrementally. */ + writeFile(path: string, contents: string): void { + if (!this.client) throw new Error("SandpackRuntime.writeFile called before mount()"); + this.files = { ...this.files, [path]: contents }; + const sandpackFiles: Record = {}; + for (const [p, code] of Object.entries(this.files)) sandpackFiles[p] = { code }; + this.client.updateSandbox({ ...this.buildSetupFrom(sandpackFiles) } as SandboxSetup); + } + + private buildSetupFrom(sandpackFiles: Record): SandboxSetup { + const env = this.entry.sandpackEnvironment ?? undefined; + const entryPath = + env && HTML_ENTRY_ENVS.has(env) && this.entry.htmlEntry + ? this.entry.htmlEntry + : this.entry.entry; + return { files: sandpackFiles, entry: entryPath, template: env } as SandboxSetup; + } + + dispose(): void { + try { + this.unlisten?.(); + } finally { + this.unlisten = null; + this.client?.destroy?.(); + this.client = null; + this.readyCbs.clear(); + this.errorCbs.clear(); + } + } +} + +/** Factory matching RuntimeFactories["sandpack"], with shared options closed over. */ +export function makeSandpackFactory(base: Omit & { version?: HandsontableVersionRef }) { + return (entry: CatalogEntry) => new SandpackRuntime(entry, base); +} diff --git a/runner/packages/runtime/src/types.ts b/runner/packages/runtime/src/types.ts index b4657ec..54638a7 100644 --- a/runner/packages/runtime/src/types.ts +++ b/runner/packages/runtime/src/types.ts @@ -13,6 +13,8 @@ export interface CatalogEntry { displayName: string; tier: Tier; sandpackTemplate: string | null; + /** Classic-bundler environment string for @codesandbox/sandpack-client (Tier 1). */ + sandpackEnvironment: string | null; container: string | null; htWrappers: string[]; entry: string; diff --git a/runner/packages/runtime/src/version.ts b/runner/packages/runtime/src/version.ts index a93e2e7..a912155 100644 --- a/runner/packages/runtime/src/version.ts +++ b/runner/packages/runtime/src/version.ts @@ -39,8 +39,11 @@ function expandPartialNumericSemver(s: string): string | null { if (!/^\d+(?:\.\d+)+$/.test(s)) return null; const parts = s.split("."); if (parts.length > 3 || parts.some((p) => !/^\d+$/.test(p))) return null; - const [major, minor = 0, patch = 0] = parts.map((p) => Number.parseInt(p, 10)); - if ([major, minor, patch].some((n) => !Number.isFinite(n) || n < 0)) return null; + const nums = parts.map((p) => Number.parseInt(p, 10)); + const major = nums[0] ?? 0; + const minor = nums[1] ?? 0; + const patch = nums[2] ?? 0; + if (![major, minor, patch].every((n) => Number.isFinite(n) && n >= 0)) return null; return `${major}.${minor}.${patch}`; } diff --git a/runner/pipeline/import.mjs b/runner/pipeline/import.mjs index f2bb1fb..32761de 100644 --- a/runner/pipeline/import.mjs +++ b/runner/pipeline/import.mjs @@ -131,6 +131,7 @@ function importExample(framework) { displayName: cfg.displayName, tier: cfg.tier, sandpackTemplate: cfg.sandpackTemplate ?? null, + sandpackEnvironment: cfg.sandpackEnvironment ?? null, container: cfg.container ?? null, htWrappers: cfg.htWrappers, entry: `/${cfg.entry}`, diff --git a/runner/pnpm-lock.yaml b/runner/pnpm-lock.yaml new file mode 100644 index 0000000..c488523 --- /dev/null +++ b/runner/pnpm-lock.yaml @@ -0,0 +1,136 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + devDependencies: + typescript: + specifier: ~5.6.0 + version: 5.6.3 + + packages/editor-shell: + dependencies: + '@handsontable/demo-runtime': + specifier: workspace:* + version: link:../runtime + + packages/runtime: + dependencies: + '@codesandbox/sandpack-client': + specifier: ^2.19.8 + version: 2.19.8 + semver: + specifier: ^7.7.4 + version: 7.8.5 + devDependencies: + '@types/semver': + specifier: ^7.5.8 + version: 7.7.1 + +packages: + + '@codesandbox/nodebox@0.1.8': + resolution: {integrity: sha512-2VRS6JDSk+M+pg56GA6CryyUSGPjBEe8Pnae0QL3jJF1mJZJVMDKr93gJRtBbLkfZN6LD/DwMtf+2L0bpWrjqg==} + + '@codesandbox/sandpack-client@2.19.8': + resolution: {integrity: sha512-CMV4nr1zgKzVpx4I3FYvGRM5YT0VaQhALMW9vy4wZRhEyWAtJITQIqZzrTGWqB1JvV7V72dVEUCUPLfYz5hgJQ==} + + '@open-draft/deferred-promise@2.2.0': + resolution: {integrity: sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==} + + '@types/semver@7.7.1': + resolution: {integrity: sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + buffer@6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + + dotenv@16.6.1: + resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} + engines: {node: '>=12'} + + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + + mime-db@1.54.0: + resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} + engines: {node: '>= 0.6'} + + outvariant@1.4.0: + resolution: {integrity: sha512-AlWY719RF02ujitly7Kk/0QlV+pXGFDHrHf9O2OKqyqgBieaPOIeuSkL8sRK6j2WK+/ZAURq2kZsY0d8JapUiw==} + + semver@7.8.5: + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} + engines: {node: '>=10'} + hasBin: true + + static-browser-server@1.0.3: + resolution: {integrity: sha512-ZUyfgGDdFRbZGGJQ1YhiM930Yczz5VlbJObrQLlk24+qNHVQx4OlLcYswEUo3bIyNAbQUIUR9Yr5/Hqjzqb4zA==} + + strict-event-emitter@0.4.6: + resolution: {integrity: sha512-12KWeb+wixJohmnwNFerbyiBrAlq5qJLwIt38etRtKtmmHyDSoGlIqFE9wx+4IwG0aDjI7GV8tc8ZccjWZZtTg==} + + typescript@5.6.3: + resolution: {integrity: sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==} + engines: {node: '>=14.17'} + hasBin: true + +snapshots: + + '@codesandbox/nodebox@0.1.8': + dependencies: + outvariant: 1.4.0 + strict-event-emitter: 0.4.6 + + '@codesandbox/sandpack-client@2.19.8': + dependencies: + '@codesandbox/nodebox': 0.1.8 + buffer: 6.0.3 + dequal: 2.0.3 + mime-db: 1.54.0 + outvariant: 1.4.0 + static-browser-server: 1.0.3 + + '@open-draft/deferred-promise@2.2.0': {} + + '@types/semver@7.7.1': {} + + base64-js@1.5.1: {} + + buffer@6.0.3: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + dequal@2.0.3: {} + + dotenv@16.6.1: {} + + ieee754@1.2.1: {} + + mime-db@1.54.0: {} + + outvariant@1.4.0: {} + + semver@7.8.5: {} + + static-browser-server@1.0.3: + dependencies: + '@open-draft/deferred-promise': 2.2.0 + dotenv: 16.6.1 + mime-db: 1.54.0 + outvariant: 1.4.0 + + strict-event-emitter@0.4.6: {} + + typescript@5.6.3: {} From 7141b3cfb044483d139be07dcef49aa5a564f26e Mon Sep 17 00:00:00 2001 From: Mateusz Wojczal Date: Wed, 8 Jul 2026 11:06:36 +0200 Subject: [PATCH 03/29] runner: unified editor shell + authoring app (Tier-1 live-editable) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Deliverable 3. - packages/editor-shell: framework-agnostic React editor. EditorShell composes a file tree, CodeMirror 6 code editor (language by extension, dark theme), a single preview-iframe slot, and a toolbar (framework tag, Handsontable version picker, Save, Share) styled from theme.ts. White-label: our logo only. The shell binds only to props/callbacks — no knowledge of Sandpack vs container. Re-keys the editor on active-file change so buffers swap correctly. - apps/authoring: Vite+React app assembling the shell with a DemoRuntime. Loads catalog.json, picks an example, mounts SandpackRuntime for Tier 1 with version dispatch, streams edits via writeFile. Tier-2 shows an editing-enabled/ preview-disabled placeholder until Deliverable 4. - Verified live in a browser against the hosted bundler: React (Vite, TS) and Vue 3 render a live Handsontable grid at v18.0.0; editing a column header updates the preview live; switching examples swaps files/engine correctly. Cloudflare Access gates this app at the edge at deploy time (no in-app auth). --- runner/apps/authoring/index.html | 16 + runner/apps/authoring/package.json | 25 + runner/apps/authoring/src/App.tsx | 162 ++ runner/apps/authoring/src/catalog.ts | 14 + runner/apps/authoring/src/main.tsx | 9 + runner/apps/authoring/tsconfig.json | 12 + runner/apps/authoring/vite.config.ts | 26 + runner/packages/editor-shell/package.json | 19 +- .../packages/editor-shell/src/CodeEditor.tsx | 65 + .../packages/editor-shell/src/EditorShell.tsx | 82 + runner/packages/editor-shell/src/FileTree.tsx | 28 + .../packages/editor-shell/src/PreviewPane.tsx | 36 + runner/packages/editor-shell/src/Toolbar.tsx | 84 + runner/packages/editor-shell/src/index.ts | 13 +- runner/packages/editor-shell/src/styles.ts | 152 ++ runner/pnpm-lock.yaml | 1421 +++++++++++++++++ 16 files changed, 2160 insertions(+), 4 deletions(-) create mode 100644 runner/apps/authoring/index.html create mode 100644 runner/apps/authoring/package.json create mode 100644 runner/apps/authoring/src/App.tsx create mode 100644 runner/apps/authoring/src/catalog.ts create mode 100644 runner/apps/authoring/src/main.tsx create mode 100644 runner/apps/authoring/tsconfig.json create mode 100644 runner/apps/authoring/vite.config.ts create mode 100644 runner/packages/editor-shell/src/CodeEditor.tsx create mode 100644 runner/packages/editor-shell/src/EditorShell.tsx create mode 100644 runner/packages/editor-shell/src/FileTree.tsx create mode 100644 runner/packages/editor-shell/src/PreviewPane.tsx create mode 100644 runner/packages/editor-shell/src/Toolbar.tsx create mode 100644 runner/packages/editor-shell/src/styles.ts diff --git a/runner/apps/authoring/index.html b/runner/apps/authoring/index.html new file mode 100644 index 0000000..cdbabdd --- /dev/null +++ b/runner/apps/authoring/index.html @@ -0,0 +1,16 @@ + + + + + + Handsontable Demos — Authoring + + + +
+ + + diff --git a/runner/apps/authoring/package.json b/runner/apps/authoring/package.json new file mode 100644 index 0000000..6ba3220 --- /dev/null +++ b/runner/apps/authoring/package.json @@ -0,0 +1,25 @@ +{ + "name": "@handsontable/demo-authoring", + "version": "0.0.0", + "private": true, + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "@handsontable/demo-editor-shell": "workspace:*", + "@handsontable/demo-runtime": "workspace:*", + "react": "^19.0.0", + "react-dom": "^19.0.0" + }, + "devDependencies": { + "@types/react": "^19.0.0", + "@types/react-dom": "^19.0.0", + "@vitejs/plugin-react": "^4.3.4", + "typescript": "~5.6.0", + "vite": "^6.0.0" + } +} diff --git a/runner/apps/authoring/src/App.tsx b/runner/apps/authoring/src/App.tsx new file mode 100644 index 0000000..073065d --- /dev/null +++ b/runner/apps/authoring/src/App.tsx @@ -0,0 +1,162 @@ +import { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { EditorShell, theme, type PreviewStatus } from "@handsontable/demo-editor-shell"; +import { + validateHandsontableVersion, + type CatalogEntry, + type DemoRuntime, + type FilesMap, +} from "@handsontable/demo-runtime"; +import { SandpackRuntime } from "@handsontable/demo-runtime/sandpack"; +import { catalog, getEntry, VERSION_OPTIONS, DEFAULT_VERSION } from "./catalog.js"; + +const SANDPACK_BUNDLER_URL = import.meta.env.VITE_SANDPACK_BUNDLER_URL || undefined; + +export function App() { + const [framework, setFramework] = useState("react"); + const entry = useMemo(() => getEntry(framework), [framework]); + + const [files, setFiles] = useState(() => ({ ...entry.files })); + const [version, setVersion] = useState(DEFAULT_VERSION); + const [status, setStatus] = useState("booting"); + const [errorMessage, setErrorMessage] = useState(null); + const [dirty, setDirty] = useState(false); + const [shareUrl, setShareUrl] = useState(null); + + const [iframeEl, setIframeEl] = useState(null); + const runtimeRef = useRef(null); + const filesRef = useRef(files); + filesRef.current = files; + + // Load fresh files when the selected example changes. + useEffect(() => { + const fresh = { ...entry.files }; + setFiles(fresh); + filesRef.current = fresh; + setDirty(false); + setShareUrl(null); + }, [entry]); + + // (Re)mount the runtime whenever the iframe, example, or version changes. + useEffect(() => { + if (!iframeEl) return; + setErrorMessage(null); + setShareUrl(null); + + if (entry.tier === 2) { + // Container engine arrives in Deliverable 4. Editing still works. + setStatus("error"); + setErrorMessage( + "Live container preview for Tier-2 frameworks arrives in Deliverable 4 — editing is enabled; preview is disabled.", + ); + return; + } + + const v = validateHandsontableVersion(version); + if (!v.ok) { + setStatus("error"); + setErrorMessage(v.message); + return; + } + + setStatus("booting"); + let cancelled = false; + const runtime = new SandpackRuntime(entry, { + iframe: iframeEl, + bundlerURL: SANDPACK_BUNDLER_URL, + version: v.value, + }); + runtime.onReady(() => !cancelled && setStatus("ready")); + runtime.onError((e) => { + if (cancelled) return; + setStatus("error"); + setErrorMessage(e.message); + }); + runtimeRef.current = runtime; + runtime.mount(filesRef.current).catch((e: unknown) => { + if (!cancelled) { + setStatus("error"); + setErrorMessage(e instanceof Error ? e.message : String(e)); + } + }); + + return () => { + cancelled = true; + runtime.dispose(); + if (runtimeRef.current === runtime) runtimeRef.current = null; + }; + }, [iframeEl, entry, version]); + + const onEdit = useCallback((path: string, contents: string) => { + setFiles((prev) => ({ ...prev, [path]: contents })); + setDirty(true); + // Tier-1: stream the edit for near-instant recompile. + try { + runtimeRef.current?.writeFile(path, contents); + } catch { + /* runtime not mounted (e.g. Tier-2 placeholder) */ + } + }, []); + + const onSave = useCallback(() => setDirty(false), []); + + const onShare = useCallback(() => { + // Sharing endpoint (snapshot -> build -> R2 -> short id) lands in Deliverable 5. + setErrorMessage("Sharing (permanent /d/:id links) arrives in Deliverable 5."); + }, []); + + return ( +
+
+ Example + + + {catalog.examples.length} examples · {SANDPACK_BUNDLER_URL ? "self-hosted bundler" : "hosted bundler"} + +
+ + +
+ ); +} diff --git a/runner/apps/authoring/src/catalog.ts b/runner/apps/authoring/src/catalog.ts new file mode 100644 index 0000000..d5fea3b --- /dev/null +++ b/runner/apps/authoring/src/catalog.ts @@ -0,0 +1,14 @@ +import type { Catalog, CatalogEntry } from "@handsontable/demo-runtime"; +import catalogJson from "../../../catalog.json"; + +export const catalog = catalogJson as unknown as Catalog; + +export function getEntry(framework: string): CatalogEntry { + const e = catalog.examples.find((x) => x.framework === framework); + if (!e) throw new Error(`unknown framework: ${framework}`); + return e; +} + +/** Curated version options for the picker; the current value is always included. */ +export const VERSION_OPTIONS = ["18.0.0", "17.6.2", "17.0.1", "16.0.0", "15.3.0"]; +export const DEFAULT_VERSION = "18.0.0"; diff --git a/runner/apps/authoring/src/main.tsx b/runner/apps/authoring/src/main.tsx new file mode 100644 index 0000000..c3e2da2 --- /dev/null +++ b/runner/apps/authoring/src/main.tsx @@ -0,0 +1,9 @@ +import { StrictMode } from "react"; +import { createRoot } from "react-dom/client"; +import { App } from "./App.js"; + +createRoot(document.getElementById("root")!).render( + + + , +); diff --git a/runner/apps/authoring/tsconfig.json b/runner/apps/authoring/tsconfig.json new file mode 100644 index 0000000..e457436 --- /dev/null +++ b/runner/apps/authoring/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "noEmit": true, + "jsx": "react-jsx", + "types": ["vite/client"], + "paths": { + "@handsontable/demo-editor-shell": ["../../packages/editor-shell/src/index.ts"] + } + }, + "include": ["src", "vite.config.ts"] +} diff --git a/runner/apps/authoring/vite.config.ts b/runner/apps/authoring/vite.config.ts new file mode 100644 index 0000000..c051aa8 --- /dev/null +++ b/runner/apps/authoring/vite.config.ts @@ -0,0 +1,26 @@ +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; +import { fileURLToPath } from "node:url"; +import path from "node:path"; + +const dir = path.dirname(fileURLToPath(import.meta.url)); +const runnerRoot = path.resolve(dir, "../.."); + +export default defineConfig({ + plugins: [react()], + resolve: { + alias: { + // Compile the editor shell as first-party TSX (it ships TS source). + "@handsontable/demo-editor-shell": path.resolve( + runnerRoot, + "packages/editor-shell/src/index.ts", + ), + }, + }, + server: { + fs: { + // Allow importing catalog.json and workspace package sources. + allow: [runnerRoot], + }, + }, +}); diff --git a/runner/packages/editor-shell/package.json b/runner/packages/editor-shell/package.json index c351c79..50a6069 100644 --- a/runner/packages/editor-shell/package.json +++ b/runner/packages/editor-shell/package.json @@ -7,7 +7,24 @@ "scripts": { "typecheck": "tsc -p tsconfig.json --noEmit" }, + "peerDependencies": { + "react": "^18 || ^19", + "react-dom": "^18 || ^19" + }, "dependencies": { - "@handsontable/demo-runtime": "workspace:*" + "@codemirror/lang-css": "^6.3.1", + "@codemirror/lang-html": "^6.4.9", + "@codemirror/lang-javascript": "^6.2.4", + "@codemirror/lang-json": "^6.0.2", + "@codemirror/lang-vue": "^0.1.3", + "@codemirror/theme-one-dark": "^6.1.3", + "@handsontable/demo-runtime": "workspace:*", + "@uiw/react-codemirror": "^4.25.2" + }, + "devDependencies": { + "@types/react": "^19.0.0", + "@types/react-dom": "^19.0.0", + "react": "^19.0.0", + "react-dom": "^19.0.0" } } diff --git a/runner/packages/editor-shell/src/CodeEditor.tsx b/runner/packages/editor-shell/src/CodeEditor.tsx new file mode 100644 index 0000000..fc979d0 --- /dev/null +++ b/runner/packages/editor-shell/src/CodeEditor.tsx @@ -0,0 +1,65 @@ +import { useMemo } from "react"; +import CodeMirror from "@uiw/react-codemirror"; +import { oneDark } from "@codemirror/theme-one-dark"; +import { javascript } from "@codemirror/lang-javascript"; +import { html } from "@codemirror/lang-html"; +import { css } from "@codemirror/lang-css"; +import { json } from "@codemirror/lang-json"; +import { vue } from "@codemirror/lang-vue"; + +function languageFor(path: string) { + const ext = path.slice(path.lastIndexOf(".")).toLowerCase(); + switch (ext) { + case ".ts": + return [javascript({ typescript: true })]; + case ".tsx": + return [javascript({ typescript: true, jsx: true })]; + case ".js": + case ".mjs": + case ".cjs": + return [javascript()]; + case ".jsx": + return [javascript({ jsx: true })]; + case ".json": + return [json()]; + case ".css": + return [css()]; + case ".vue": + return [vue()]; + case ".html": + case ".astro": // close enough for highlighting; Astro has no dedicated CM6 mode + return [html()]; + default: + return []; + } +} + +export interface CodeEditorProps { + path: string; + value: string; + onChange: (value: string) => void; + readOnly?: boolean; +} + +/** Thin CodeMirror wrapper: picks a language by file extension, dark theme. */ +export function CodeEditor({ path, value, onChange, readOnly }: CodeEditorProps) { + const extensions = useMemo(() => languageFor(path), [path]); + return ( + + ); +} diff --git a/runner/packages/editor-shell/src/EditorShell.tsx b/runner/packages/editor-shell/src/EditorShell.tsx new file mode 100644 index 0000000..b78f48a --- /dev/null +++ b/runner/packages/editor-shell/src/EditorShell.tsx @@ -0,0 +1,82 @@ +import { useEffect, useMemo, useState } from "react"; +import type { FilesMap } from "@handsontable/demo-runtime"; +import { CodeEditor } from "./CodeEditor.js"; +import { FileTree } from "./FileTree.js"; +import { Toolbar } from "./Toolbar.js"; +import { PreviewPane, type PreviewStatus } from "./PreviewPane.js"; +import { s } from "./styles.js"; + +export interface EditorShellProps { + frameworkLabel: string; + files: FilesMap; + /** Entry path to open first (e.g. "/src/index.tsx"). */ + entry: string; + /** Preview iframe binding — the app attaches its DemoRuntime here. */ + iframeRef: (el: HTMLIFrameElement | null) => void; + status: PreviewStatus; + errorMessage?: string | null; + + version: string; + versionOptions: string[]; + onVersionChange: (v: string) => void; + + /** Fired on every edit. The app updates its files map and calls runtime.writeFile. */ + onEdit: (path: string, contents: string) => void; + onSave: () => void; + onShare: () => void; + sharing?: boolean; + shareUrl?: string | null; + dirty?: boolean; +} + +/** + * Framework-agnostic editor: file tree | code editor | live preview + toolbar. + * Binds only to props — it has no knowledge of Sandpack vs container. + */ +export function EditorShell(props: EditorShellProps) { + const paths = useMemo(() => Object.keys(props.files), [props.files]); + const [active, setActive] = useState(() => + props.files[props.entry] !== undefined ? props.entry : (paths[0] ?? ""), + ); + + // Keep the active file valid if the file set changes (e.g. example switch). + useEffect(() => { + if (props.files[active] === undefined) { + setActive(props.files[props.entry] !== undefined ? props.entry : (paths[0] ?? "")); + } + }, [props.files, props.entry, active, paths]); + + return ( +
+ +
+ +
+ {active && ( + props.onEdit(active, v)} + /> + )} +
+ +
+
+ ); +} diff --git a/runner/packages/editor-shell/src/FileTree.tsx b/runner/packages/editor-shell/src/FileTree.tsx new file mode 100644 index 0000000..cedc0ba --- /dev/null +++ b/runner/packages/editor-shell/src/FileTree.tsx @@ -0,0 +1,28 @@ +import { s } from "./styles.js"; + +export interface FileTreeProps { + paths: string[]; + active: string; + onSelect: (path: string) => void; +} + +/** Flat, sorted file list. A nested tree can replace this without touching the + * shell's contract — the editor only needs (paths, active, onSelect). */ +export function FileTree({ paths, active, onSelect }: FileTreeProps) { + const sorted = [...paths].sort((a, b) => a.localeCompare(b)); + return ( + + ); +} diff --git a/runner/packages/editor-shell/src/PreviewPane.tsx b/runner/packages/editor-shell/src/PreviewPane.tsx new file mode 100644 index 0000000..6f2bdfd --- /dev/null +++ b/runner/packages/editor-shell/src/PreviewPane.tsx @@ -0,0 +1,36 @@ +import { s } from "./styles.js"; + +export type PreviewStatus = "booting" | "ready" | "error"; + +export interface PreviewPaneProps { + /** Callback ref: the parent binds its DemoRuntime to this iframe. */ + iframeRef: (el: HTMLIFrameElement | null) => void; + status: PreviewStatus; + errorMessage?: string | null; +} + +const STATUS_TEXT: Record = { + booting: "Booting preview…", + ready: "Live", + error: "Error", +}; + +/** The single preview-iframe slot. Identical for Tier 1 (Sandpack drives the + * iframe) and Tier 2 (iframe.src = container preview URL) — the shell never + * knows which engine is behind it. */ +export function PreviewPane({ iframeRef, status, errorMessage }: PreviewPaneProps) { + return ( +
+
+ {STATUS_TEXT[status]} + {status === "error" && errorMessage ? `: ${errorMessage}` : ""} +
+