diff --git a/.prettierignore b/.prettierignore index 3c162167..6e729b85 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,4 +1,6 @@ /dist /docs /generated -/public \ No newline at end of file +/public + +README.md diff --git a/README.md b/README.md index 0d862e2f..7ed4faf5 100644 --- a/README.md +++ b/README.md @@ -35,9 +35,7 @@ Documentation for this project is available at [react-window.vercel.app](https:/ ### List - Renders data with many rows. - #### Required props @@ -164,9 +162,7 @@ The default value is "div", meaning that List renders an HTMLDivElemen ### Grid - Renders data with many rows and columns. - #### Required props diff --git a/eslint.config.js b/eslint.config.js index d7e0e28f..ce4dfc5f 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -6,7 +6,7 @@ import tseslint from "typescript-eslint"; import { globalIgnores } from "eslint/config"; export default tseslint.config([ - globalIgnores(["dist", "docs", "generated"]), + globalIgnores(["dist", "docs", "public/generated"]), { files: ["**/*.{ts,tsx}"], extends: [ diff --git a/index.css b/index.css index 11a84d17..2953f09b 100644 --- a/index.css +++ b/index.css @@ -1,92 +1,14 @@ +@source "node_modules/react-lib-tools"; + @import "tailwindcss"; +@import "react-lib-tools/styles.css"; :root { - font-family: system-ui, Avenir, Helvetica, Arial, sans-serif; - line-height: 1.5; - font-weight: 400; - font-size: 12px; - - @media only screen and (max-width: 600px) { - font-size: 16px; - } - - color-scheme: dark; - font-synthesis: none; - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -@keyframes background-gradient-animation { - 0% { - background-position: 0% 50%; - } - 50% { - background-position: 100% 50%; - } - 100% { - background-position: 0% 50%; - } -} - -#root { - width: 100dvw; - height: 100dvh; - overflow: auto; - background: linear-gradient( - -45deg, - var(--color-pink-500), - var(--color-sky-500), - var(--color-emerald-400) - ); - background-size: 400% 400%; - animation: background-gradient-animation 20s ease infinite; -} - -* { - scrollbar-width: thin; - scrollbar-color: var(--color-slate-600) transparent; - outline: none; - transition: - color 0.25s ease, - background-color 0.25s ease, - border-color 0.25s ease, - opacity 0.25s ease, - outline-color 0.25s ease; -} - -main { - [data-link], - a { - cursor: pointer; - color: var(--color-teal-400); - &:hover { - color: var(--color-teal-300); - } - } -} - -*[data-focus] { - border: 2px solid transparent; - &:focus { - border: 2px solid var(--color-teal-300); - } -} - -*[data-focus-within] { - border: 2px solid transparent; - &:focus-within { - border: 2px solid var(--color-teal-300); - } - - &[data-focus-within="bold"] { - border-color: var(--color-teal-600); - &:focus-within { - border: 2px solid var(--color-teal-300); - } - } -} + --template-gradient-color-1: var(--color-indigo-500); + --template-gradient-color-2: var(--color-sky-500); + --template-gradient-color-3: var(--color-emerald-400); -code { - color: rgba(255, 255, 255, 0.8); + --template-focus-color-300: var(--color-teal-300); + --template-focus-color-400: var(--color-teal-400); + --template-focus-color-600: var(--color-teal-600); } diff --git a/package.json b/package.json index 84796e46..44fc499d 100644 --- a/package.json +++ b/package.json @@ -23,9 +23,9 @@ "build": "pnpm run build:lib && pnpm run build:docs", "build:docs": "TARGET=docs vite build", "build:lib": "TARGET=lib vite build", - "compile": "pnpm run compile:code-snippets && pnpm run compile:docs", - "compile:code-snippets": "node --loader ts-node/esm ./scripts/compile-code-snippets.ts", - "compile:docs": "node --loader ts-node/esm ./scripts/compile-docs.ts", + "compile": "pnpm run compile:docs && pnpm run compile:examples", + "compile:docs": "tsx ./scripts/compile-docs", + "compile:examples": "tsx ./scripts/compile-examples", "lint": "eslint .", "prerelease": "rm -rf dist && pnpm run build:lib", "prettier": "prettier --write \"**/*.{css,html,js,json,jsx,ts,tsx}\"", @@ -85,6 +85,7 @@ "react-docgen-typescript": "^2.4.0", "react-dom": "^19.2.3", "react-error-boundary": "^6.0.0", + "react-lib-tools": "^0.0.6", "react-router-dom": "^7.6.3", "rollup-plugin-terser": "^7.0.2", "rollup-plugin-visualizer": "^6.0.3", @@ -92,6 +93,7 @@ "tailwindcss": "^4.1.11", "ts-blank-space": "^0.6.2", "ts-node": "^10.9.2", + "tsx": "^4.21.0", "typescript": "~5.8.3", "typescript-eslint": "^8.35.1", "typescript-json-schema": "^0.65.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c0fe844b..514f14c8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -45,7 +45,7 @@ importers: version: 1.2.1 "@tailwindcss/vite": specifier: ^4.1.11 - version: 4.1.11(vite@7.0.6(@types/node@24.2.0)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0)) + version: 4.1.11(vite@7.0.6(@types/node@24.2.0)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.21.0)(yaml@2.8.0)) "@tailwindplus/elements": specifier: ^1.0.5 version: 1.0.5 @@ -75,7 +75,7 @@ importers: version: 19.2.3(@types/react@19.2.7) "@vitejs/plugin-react-swc": specifier: ^3.10.2 - version: 3.10.2(@swc/helpers@0.5.17)(vite@7.0.6(@types/node@24.2.0)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0)) + version: 3.10.2(@swc/helpers@0.5.17)(vite@7.0.6(@types/node@24.2.0)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.21.0)(yaml@2.8.0)) clsx: specifier: ^2.1.1 version: 2.1.1 @@ -130,6 +130,9 @@ importers: react-error-boundary: specifier: ^6.0.0 version: 6.0.0(react@19.2.3) + react-lib-tools: + specifier: ^0.0.6 + version: 0.0.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3) react-router-dom: specifier: ^7.6.3 version: 7.6.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3) @@ -151,6 +154,9 @@ importers: ts-node: specifier: ^10.9.2 version: 10.9.2(@swc/core@1.12.9(@swc/helpers@0.5.17))(@types/node@24.2.0)(typescript@5.8.3) + tsx: + specifier: ^4.21.0 + version: 4.21.0 typescript: specifier: ~5.8.3 version: 5.8.3 @@ -162,16 +168,16 @@ importers: version: 0.65.1(@swc/core@1.12.9(@swc/helpers@0.5.17)) vite: specifier: ^7.0.4 - version: 7.0.6(@types/node@24.2.0)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0) + version: 7.0.6(@types/node@24.2.0)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.21.0)(yaml@2.8.0) vite-plugin-dts: specifier: ^4.5.4 - version: 4.5.4(@types/node@24.2.0)(rollup@4.44.2)(typescript@5.8.3)(vite@7.0.6(@types/node@24.2.0)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0)) + version: 4.5.4(@types/node@24.2.0)(rollup@4.44.2)(typescript@5.8.3)(vite@7.0.6(@types/node@24.2.0)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.21.0)(yaml@2.8.0)) vite-plugin-svgr: specifier: ^4.3.0 - version: 4.3.0(rollup@4.44.2)(typescript@5.8.3)(vite@7.0.6(@types/node@24.2.0)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0)) + version: 4.3.0(rollup@4.44.2)(typescript@5.8.3)(vite@7.0.6(@types/node@24.2.0)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.21.0)(yaml@2.8.0)) vitest: specifier: ^3.2.4 - version: 3.2.4(@types/debug@4.1.12)(@types/node@24.2.0)(jiti@2.4.2)(jsdom@26.1.0)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0) + version: 3.2.4(@types/debug@4.1.12)(@types/node@24.2.0)(jiti@2.4.2)(jsdom@26.1.0)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.21.0)(yaml@2.8.0) zustand: specifier: ^5.0.7 version: 5.0.7(@types/react@19.2.7)(react@19.2.3)(use-sync-external-store@1.5.0(react@19.2.3)) @@ -475,6 +481,15 @@ packages: cpu: [ppc64] os: [aix] + "@esbuild/aix-ppc64@0.27.2": + resolution: + { + integrity: sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw== + } + engines: { node: ">=18" } + cpu: [ppc64] + os: [aix] + "@esbuild/android-arm64@0.25.5": resolution: { @@ -484,6 +499,15 @@ packages: cpu: [arm64] os: [android] + "@esbuild/android-arm64@0.27.2": + resolution: + { + integrity: sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA== + } + engines: { node: ">=18" } + cpu: [arm64] + os: [android] + "@esbuild/android-arm@0.25.5": resolution: { @@ -493,6 +517,15 @@ packages: cpu: [arm] os: [android] + "@esbuild/android-arm@0.27.2": + resolution: + { + integrity: sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA== + } + engines: { node: ">=18" } + cpu: [arm] + os: [android] + "@esbuild/android-x64@0.25.5": resolution: { @@ -502,6 +535,15 @@ packages: cpu: [x64] os: [android] + "@esbuild/android-x64@0.27.2": + resolution: + { + integrity: sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A== + } + engines: { node: ">=18" } + cpu: [x64] + os: [android] + "@esbuild/darwin-arm64@0.25.5": resolution: { @@ -511,6 +553,15 @@ packages: cpu: [arm64] os: [darwin] + "@esbuild/darwin-arm64@0.27.2": + resolution: + { + integrity: sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg== + } + engines: { node: ">=18" } + cpu: [arm64] + os: [darwin] + "@esbuild/darwin-x64@0.25.5": resolution: { @@ -520,6 +571,15 @@ packages: cpu: [x64] os: [darwin] + "@esbuild/darwin-x64@0.27.2": + resolution: + { + integrity: sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA== + } + engines: { node: ">=18" } + cpu: [x64] + os: [darwin] + "@esbuild/freebsd-arm64@0.25.5": resolution: { @@ -529,6 +589,15 @@ packages: cpu: [arm64] os: [freebsd] + "@esbuild/freebsd-arm64@0.27.2": + resolution: + { + integrity: sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g== + } + engines: { node: ">=18" } + cpu: [arm64] + os: [freebsd] + "@esbuild/freebsd-x64@0.25.5": resolution: { @@ -538,6 +607,15 @@ packages: cpu: [x64] os: [freebsd] + "@esbuild/freebsd-x64@0.27.2": + resolution: + { + integrity: sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA== + } + engines: { node: ">=18" } + cpu: [x64] + os: [freebsd] + "@esbuild/linux-arm64@0.25.5": resolution: { @@ -547,6 +625,15 @@ packages: cpu: [arm64] os: [linux] + "@esbuild/linux-arm64@0.27.2": + resolution: + { + integrity: sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw== + } + engines: { node: ">=18" } + cpu: [arm64] + os: [linux] + "@esbuild/linux-arm@0.25.5": resolution: { @@ -556,6 +643,15 @@ packages: cpu: [arm] os: [linux] + "@esbuild/linux-arm@0.27.2": + resolution: + { + integrity: sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw== + } + engines: { node: ">=18" } + cpu: [arm] + os: [linux] + "@esbuild/linux-ia32@0.25.5": resolution: { @@ -565,6 +661,15 @@ packages: cpu: [ia32] os: [linux] + "@esbuild/linux-ia32@0.27.2": + resolution: + { + integrity: sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w== + } + engines: { node: ">=18" } + cpu: [ia32] + os: [linux] + "@esbuild/linux-loong64@0.25.5": resolution: { @@ -574,6 +679,15 @@ packages: cpu: [loong64] os: [linux] + "@esbuild/linux-loong64@0.27.2": + resolution: + { + integrity: sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg== + } + engines: { node: ">=18" } + cpu: [loong64] + os: [linux] + "@esbuild/linux-mips64el@0.25.5": resolution: { @@ -583,6 +697,15 @@ packages: cpu: [mips64el] os: [linux] + "@esbuild/linux-mips64el@0.27.2": + resolution: + { + integrity: sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw== + } + engines: { node: ">=18" } + cpu: [mips64el] + os: [linux] + "@esbuild/linux-ppc64@0.25.5": resolution: { @@ -592,6 +715,15 @@ packages: cpu: [ppc64] os: [linux] + "@esbuild/linux-ppc64@0.27.2": + resolution: + { + integrity: sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ== + } + engines: { node: ">=18" } + cpu: [ppc64] + os: [linux] + "@esbuild/linux-riscv64@0.25.5": resolution: { @@ -601,6 +733,15 @@ packages: cpu: [riscv64] os: [linux] + "@esbuild/linux-riscv64@0.27.2": + resolution: + { + integrity: sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA== + } + engines: { node: ">=18" } + cpu: [riscv64] + os: [linux] + "@esbuild/linux-s390x@0.25.5": resolution: { @@ -610,6 +751,15 @@ packages: cpu: [s390x] os: [linux] + "@esbuild/linux-s390x@0.27.2": + resolution: + { + integrity: sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w== + } + engines: { node: ">=18" } + cpu: [s390x] + os: [linux] + "@esbuild/linux-x64@0.25.5": resolution: { @@ -619,6 +769,15 @@ packages: cpu: [x64] os: [linux] + "@esbuild/linux-x64@0.27.2": + resolution: + { + integrity: sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA== + } + engines: { node: ">=18" } + cpu: [x64] + os: [linux] + "@esbuild/netbsd-arm64@0.25.5": resolution: { @@ -628,6 +787,15 @@ packages: cpu: [arm64] os: [netbsd] + "@esbuild/netbsd-arm64@0.27.2": + resolution: + { + integrity: sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw== + } + engines: { node: ">=18" } + cpu: [arm64] + os: [netbsd] + "@esbuild/netbsd-x64@0.25.5": resolution: { @@ -637,6 +805,15 @@ packages: cpu: [x64] os: [netbsd] + "@esbuild/netbsd-x64@0.27.2": + resolution: + { + integrity: sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA== + } + engines: { node: ">=18" } + cpu: [x64] + os: [netbsd] + "@esbuild/openbsd-arm64@0.25.5": resolution: { @@ -646,6 +823,15 @@ packages: cpu: [arm64] os: [openbsd] + "@esbuild/openbsd-arm64@0.27.2": + resolution: + { + integrity: sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA== + } + engines: { node: ">=18" } + cpu: [arm64] + os: [openbsd] + "@esbuild/openbsd-x64@0.25.5": resolution: { @@ -655,6 +841,24 @@ packages: cpu: [x64] os: [openbsd] + "@esbuild/openbsd-x64@0.27.2": + resolution: + { + integrity: sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg== + } + engines: { node: ">=18" } + cpu: [x64] + os: [openbsd] + + "@esbuild/openharmony-arm64@0.27.2": + resolution: + { + integrity: sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag== + } + engines: { node: ">=18" } + cpu: [arm64] + os: [openharmony] + "@esbuild/sunos-x64@0.25.5": resolution: { @@ -664,6 +868,15 @@ packages: cpu: [x64] os: [sunos] + "@esbuild/sunos-x64@0.27.2": + resolution: + { + integrity: sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg== + } + engines: { node: ">=18" } + cpu: [x64] + os: [sunos] + "@esbuild/win32-arm64@0.25.5": resolution: { @@ -673,6 +886,15 @@ packages: cpu: [arm64] os: [win32] + "@esbuild/win32-arm64@0.27.2": + resolution: + { + integrity: sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg== + } + engines: { node: ">=18" } + cpu: [arm64] + os: [win32] + "@esbuild/win32-ia32@0.25.5": resolution: { @@ -682,6 +904,15 @@ packages: cpu: [ia32] os: [win32] + "@esbuild/win32-ia32@0.27.2": + resolution: + { + integrity: sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ== + } + engines: { node: ">=18" } + cpu: [ia32] + os: [win32] + "@esbuild/win32-x64@0.25.5": resolution: { @@ -691,6 +922,15 @@ packages: cpu: [x64] os: [win32] + "@esbuild/win32-x64@0.27.2": + resolution: + { + integrity: sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ== + } + engines: { node: ">=18" } + cpu: [x64] + os: [win32] + "@eslint-community/eslint-utils@4.7.0": resolution: { @@ -2609,6 +2849,14 @@ packages: engines: { node: ">=18" } hasBin: true + esbuild@0.27.2: + resolution: + { + integrity: sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw== + } + engines: { node: ">=18" } + hasBin: true + escalade@3.2.0: resolution: { @@ -2864,6 +3112,12 @@ packages: } engines: { node: ">=18" } + get-tsconfig@4.13.0: + resolution: + { + integrity: sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ== + } + glob-parent@5.1.2: resolution: { @@ -3924,6 +4178,15 @@ packages: integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== } + react-lib-tools@0.0.6: + resolution: + { + integrity: sha512-1oJB9AqKSL/wmUzBr98PDXeNCuWhKFZs1A+XDOQsNbgSscXPSmoHle0D+sNmRwjKw5Fw5JHKYPcpISaVIwk/3g== + } + peerDependencies: + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + react-router-dom@7.6.3: resolution: { @@ -3982,6 +4245,12 @@ packages: } engines: { node: ">=4" } + resolve-pkg-maps@1.0.0: + resolution: + { + integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw== + } + resolve@1.22.10: resolution: { @@ -4468,6 +4737,14 @@ packages: integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== } + tsx@4.21.0: + resolution: + { + integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw== + } + engines: { node: ">=18.0.0" } + hasBin: true + type-check@0.4.0: resolution: { @@ -5131,78 +5408,156 @@ snapshots: "@esbuild/aix-ppc64@0.25.5": optional: true + "@esbuild/aix-ppc64@0.27.2": + optional: true + "@esbuild/android-arm64@0.25.5": optional: true + "@esbuild/android-arm64@0.27.2": + optional: true + "@esbuild/android-arm@0.25.5": optional: true + "@esbuild/android-arm@0.27.2": + optional: true + "@esbuild/android-x64@0.25.5": optional: true + "@esbuild/android-x64@0.27.2": + optional: true + "@esbuild/darwin-arm64@0.25.5": optional: true + "@esbuild/darwin-arm64@0.27.2": + optional: true + "@esbuild/darwin-x64@0.25.5": optional: true + "@esbuild/darwin-x64@0.27.2": + optional: true + "@esbuild/freebsd-arm64@0.25.5": optional: true + "@esbuild/freebsd-arm64@0.27.2": + optional: true + "@esbuild/freebsd-x64@0.25.5": optional: true + "@esbuild/freebsd-x64@0.27.2": + optional: true + "@esbuild/linux-arm64@0.25.5": optional: true + "@esbuild/linux-arm64@0.27.2": + optional: true + "@esbuild/linux-arm@0.25.5": optional: true + "@esbuild/linux-arm@0.27.2": + optional: true + "@esbuild/linux-ia32@0.25.5": optional: true + "@esbuild/linux-ia32@0.27.2": + optional: true + "@esbuild/linux-loong64@0.25.5": optional: true + "@esbuild/linux-loong64@0.27.2": + optional: true + "@esbuild/linux-mips64el@0.25.5": optional: true + "@esbuild/linux-mips64el@0.27.2": + optional: true + "@esbuild/linux-ppc64@0.25.5": optional: true + "@esbuild/linux-ppc64@0.27.2": + optional: true + "@esbuild/linux-riscv64@0.25.5": optional: true + "@esbuild/linux-riscv64@0.27.2": + optional: true + "@esbuild/linux-s390x@0.25.5": optional: true + "@esbuild/linux-s390x@0.27.2": + optional: true + "@esbuild/linux-x64@0.25.5": optional: true + "@esbuild/linux-x64@0.27.2": + optional: true + "@esbuild/netbsd-arm64@0.25.5": optional: true + "@esbuild/netbsd-arm64@0.27.2": + optional: true + "@esbuild/netbsd-x64@0.25.5": optional: true + "@esbuild/netbsd-x64@0.27.2": + optional: true + "@esbuild/openbsd-arm64@0.25.5": optional: true + "@esbuild/openbsd-arm64@0.27.2": + optional: true + "@esbuild/openbsd-x64@0.25.5": optional: true + "@esbuild/openbsd-x64@0.27.2": + optional: true + + "@esbuild/openharmony-arm64@0.27.2": + optional: true + "@esbuild/sunos-x64@0.25.5": optional: true + "@esbuild/sunos-x64@0.27.2": + optional: true + "@esbuild/win32-arm64@0.25.5": optional: true + "@esbuild/win32-arm64@0.27.2": + optional: true + "@esbuild/win32-ia32@0.25.5": optional: true + "@esbuild/win32-ia32@0.27.2": + optional: true + "@esbuild/win32-x64@0.25.5": optional: true + "@esbuild/win32-x64@0.27.2": + optional: true + "@eslint-community/eslint-utils@4.7.0(eslint@9.30.1(jiti@2.4.2))": dependencies: eslint: 9.30.1(jiti@2.4.2) @@ -5769,12 +6124,12 @@ snapshots: "@tailwindcss/oxide-win32-arm64-msvc": 4.1.11 "@tailwindcss/oxide-win32-x64-msvc": 4.1.11 - "@tailwindcss/vite@4.1.11(vite@7.0.6(@types/node@24.2.0)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0))": + "@tailwindcss/vite@4.1.11(vite@7.0.6(@types/node@24.2.0)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.21.0)(yaml@2.8.0))": dependencies: "@tailwindcss/node": 4.1.11 "@tailwindcss/oxide": 4.1.11 tailwindcss: 4.1.11 - vite: 7.0.6(@types/node@24.2.0)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0) + vite: 7.0.6(@types/node@24.2.0)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.21.0)(yaml@2.8.0) "@tailwindplus/elements@1.0.5": {} @@ -5979,11 +6334,11 @@ snapshots: "@typescript-eslint/types": 8.35.1 eslint-visitor-keys: 4.2.1 - "@vitejs/plugin-react-swc@3.10.2(@swc/helpers@0.5.17)(vite@7.0.6(@types/node@24.2.0)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0))": + "@vitejs/plugin-react-swc@3.10.2(@swc/helpers@0.5.17)(vite@7.0.6(@types/node@24.2.0)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.21.0)(yaml@2.8.0))": dependencies: "@rolldown/pluginutils": 1.0.0-beta.11 "@swc/core": 1.12.9(@swc/helpers@0.5.17) - vite: 7.0.6(@types/node@24.2.0)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0) + vite: 7.0.6(@types/node@24.2.0)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.21.0)(yaml@2.8.0) transitivePeerDependencies: - "@swc/helpers" @@ -5995,13 +6350,13 @@ snapshots: chai: 5.2.1 tinyrainbow: 2.0.0 - "@vitest/mocker@3.2.4(vite@7.0.6(@types/node@24.2.0)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0))": + "@vitest/mocker@3.2.4(vite@7.0.6(@types/node@24.2.0)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.21.0)(yaml@2.8.0))": dependencies: "@vitest/spy": 3.2.4 estree-walker: 3.0.3 magic-string: 0.30.17 optionalDependencies: - vite: 7.0.6(@types/node@24.2.0)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0) + vite: 7.0.6(@types/node@24.2.0)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.21.0)(yaml@2.8.0) "@vitest/pretty-format@3.2.4": dependencies: @@ -6356,6 +6711,35 @@ snapshots: "@esbuild/win32-ia32": 0.25.5 "@esbuild/win32-x64": 0.25.5 + esbuild@0.27.2: + optionalDependencies: + "@esbuild/aix-ppc64": 0.27.2 + "@esbuild/android-arm": 0.27.2 + "@esbuild/android-arm64": 0.27.2 + "@esbuild/android-x64": 0.27.2 + "@esbuild/darwin-arm64": 0.27.2 + "@esbuild/darwin-x64": 0.27.2 + "@esbuild/freebsd-arm64": 0.27.2 + "@esbuild/freebsd-x64": 0.27.2 + "@esbuild/linux-arm": 0.27.2 + "@esbuild/linux-arm64": 0.27.2 + "@esbuild/linux-ia32": 0.27.2 + "@esbuild/linux-loong64": 0.27.2 + "@esbuild/linux-mips64el": 0.27.2 + "@esbuild/linux-ppc64": 0.27.2 + "@esbuild/linux-riscv64": 0.27.2 + "@esbuild/linux-s390x": 0.27.2 + "@esbuild/linux-x64": 0.27.2 + "@esbuild/netbsd-arm64": 0.27.2 + "@esbuild/netbsd-x64": 0.27.2 + "@esbuild/openbsd-arm64": 0.27.2 + "@esbuild/openbsd-x64": 0.27.2 + "@esbuild/openharmony-arm64": 0.27.2 + "@esbuild/sunos-x64": 0.27.2 + "@esbuild/win32-arm64": 0.27.2 + "@esbuild/win32-ia32": 0.27.2 + "@esbuild/win32-x64": 0.27.2 + escalade@3.2.0: {} escape-string-regexp@4.0.0: {} @@ -6510,6 +6894,10 @@ snapshots: get-east-asian-width@1.3.0: {} + get-tsconfig@4.13.0: + dependencies: + resolve-pkg-maps: 1.0.0 + glob-parent@5.1.2: dependencies: is-glob: 4.0.3 @@ -7037,6 +7425,11 @@ snapshots: react-is@17.0.2: {} + react-lib-tools@0.0.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3): + dependencies: + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + react-router-dom@7.6.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3): dependencies: react: 19.2.3 @@ -7064,6 +7457,8 @@ snapshots: resolve-from@4.0.0: {} + resolve-pkg-maps@1.0.0: {} + resolve@1.22.10: dependencies: is-core-module: 2.16.1 @@ -7353,6 +7748,13 @@ snapshots: tslib@2.8.1: {} + tsx@4.21.0: + dependencies: + esbuild: 0.27.2 + get-tsconfig: 4.13.0 + optionalDependencies: + fsevents: 2.3.3 + type-check@0.4.0: dependencies: prelude-ls: 1.2.1 @@ -7415,13 +7817,13 @@ snapshots: v8-compile-cache-lib@3.0.1: {} - vite-node@3.2.4(@types/node@24.2.0)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0): + vite-node@3.2.4(@types/node@24.2.0)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.21.0)(yaml@2.8.0): dependencies: cac: 6.7.14 debug: 4.4.1 es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 7.0.6(@types/node@24.2.0)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0) + vite: 7.0.6(@types/node@24.2.0)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.21.0)(yaml@2.8.0) transitivePeerDependencies: - "@types/node" - jiti @@ -7436,7 +7838,7 @@ snapshots: - tsx - yaml - vite-plugin-dts@4.5.4(@types/node@24.2.0)(rollup@4.44.2)(typescript@5.8.3)(vite@7.0.6(@types/node@24.2.0)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0)): + vite-plugin-dts@4.5.4(@types/node@24.2.0)(rollup@4.44.2)(typescript@5.8.3)(vite@7.0.6(@types/node@24.2.0)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.21.0)(yaml@2.8.0)): dependencies: "@microsoft/api-extractor": 7.52.10(@types/node@24.2.0) "@rollup/pluginutils": 5.2.0(rollup@4.44.2) @@ -7449,24 +7851,24 @@ snapshots: magic-string: 0.30.17 typescript: 5.8.3 optionalDependencies: - vite: 7.0.6(@types/node@24.2.0)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0) + vite: 7.0.6(@types/node@24.2.0)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.21.0)(yaml@2.8.0) transitivePeerDependencies: - "@types/node" - rollup - supports-color - vite-plugin-svgr@4.3.0(rollup@4.44.2)(typescript@5.8.3)(vite@7.0.6(@types/node@24.2.0)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0)): + vite-plugin-svgr@4.3.0(rollup@4.44.2)(typescript@5.8.3)(vite@7.0.6(@types/node@24.2.0)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.21.0)(yaml@2.8.0)): dependencies: "@rollup/pluginutils": 5.2.0(rollup@4.44.2) "@svgr/core": 8.1.0(typescript@5.8.3) "@svgr/plugin-jsx": 8.1.0(@svgr/core@8.1.0(typescript@5.8.3)) - vite: 7.0.6(@types/node@24.2.0)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0) + vite: 7.0.6(@types/node@24.2.0)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.21.0)(yaml@2.8.0) transitivePeerDependencies: - rollup - supports-color - typescript - vite@7.0.6(@types/node@24.2.0)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0): + vite@7.0.6(@types/node@24.2.0)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.21.0)(yaml@2.8.0): dependencies: esbuild: 0.25.5 fdir: 6.4.6(picomatch@4.0.3) @@ -7480,13 +7882,14 @@ snapshots: jiti: 2.4.2 lightningcss: 1.30.1 terser: 5.43.1 + tsx: 4.21.0 yaml: 2.8.0 - vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.2.0)(jiti@2.4.2)(jsdom@26.1.0)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0): + vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.2.0)(jiti@2.4.2)(jsdom@26.1.0)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.21.0)(yaml@2.8.0): dependencies: "@types/chai": 5.2.2 "@vitest/expect": 3.2.4 - "@vitest/mocker": 3.2.4(vite@7.0.6(@types/node@24.2.0)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0)) + "@vitest/mocker": 3.2.4(vite@7.0.6(@types/node@24.2.0)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.21.0)(yaml@2.8.0)) "@vitest/pretty-format": 3.2.4 "@vitest/runner": 3.2.4 "@vitest/snapshot": 3.2.4 @@ -7504,8 +7907,8 @@ snapshots: tinyglobby: 0.2.14 tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 7.0.6(@types/node@24.2.0)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0) - vite-node: 3.2.4(@types/node@24.2.0)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.0) + vite: 7.0.6(@types/node@24.2.0)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.21.0)(yaml@2.8.0) + vite-node: 3.2.4(@types/node@24.2.0)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.21.0)(yaml@2.8.0) why-is-node-running: 2.3.0 optionalDependencies: "@types/debug": 4.1.12 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 00000000..933b8c75 --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,3 @@ +packages: + - lib/* + - src/* diff --git a/public/generated/README.md b/public/generated/README.md deleted file mode 100644 index c8936681..00000000 --- a/public/generated/README.md +++ /dev/null @@ -1 +0,0 @@ -The contents of this folder are generated automatically. Do not edit them by hand. \ No newline at end of file diff --git a/public/generated/js-docs/Grid.json b/public/generated/docs/Grid.json similarity index 73% rename from public/generated/js-docs/Grid.json rename to public/generated/docs/Grid.json index ea10e176..d0ea4983 100644 --- a/public/generated/js-docs/Grid.json +++ b/public/generated/docs/Grid.json @@ -108,7 +108,7 @@ "content": "

Default height of grid for initial render.\nThis value is important for server rendering.

\n" } ], - "html": "
defaultHeight?: number = 0
", + "html": "
defaultHeight?: number = \"0\"
", "name": "defaultHeight", "required": false }, @@ -118,7 +118,7 @@ "content": "

Default width of grid for initial render.\nThis value is important for server rendering.

\n" } ], - "html": "
defaultWidth?: number = 0
", + "html": "
defaultWidth?: number = \"0\"
", "name": "defaultWidth", "required": false }, @@ -132,7 +132,7 @@ "intent": "primary" } ], - "html": "
gridRef?: Ref<{ readonly element: HTMLDivElement | null; scrollToCell(config: { behavior?: \"auto\" | \"instant\" | \"smooth\"; columnAlign?: \"auto\" | \"center\" | \"end\" | \"smart\" | \"start\" | undefined; columnIndex: number; rowAlign?: \"auto\" | ... 4 more ... | undefined; rowIndex: number; }): void; scrollToColumn(config: ...
", + "html": "
gridRef?: Ref<{ readonly element: HTMLDivElement | null; scrollToCell(config: { behavior?: \"auto\" | \"instant\" | \"smooth\"; columnAlign?: \"auto\" | \"center\" | \"end\" | \"smart\" | \"start\"; columnIndex: number; rowAlign?: \"auto\" | ... 3 more ... | \"start\"; rowIndex: number; }): void; scrollToColumn(config: { ...; }): void; scrollToR...
", "name": "gridRef", "required": false }, @@ -142,7 +142,7 @@ "content": "

Callback notified when the range of rendered cells changes.

\n" } ], - "html": "
onCellsRendered?: ((visibleCells: { columnStartIndex: number; columnStopIndex: number; rowStartIndex: number; rowStopIndex: number; }, allCells: { columnStartIndex: number; columnStopIndex: number; rowStartIndex: number; rowStopIndex: number; }) => void)
", + "html": "
onCellsRendered?: (visibleCells: { columnStartIndex: number; columnStopIndex: number; rowStartIndex: number; rowStopIndex: number; }, allCells: { columnStartIndex: number; columnStopIndex: number; rowStartIndex: number; rowStopIndex: number; }) => void
", "name": "onCellsRendered", "required": false }, @@ -152,7 +152,7 @@ "content": "

Callback notified when the Grid's outermost HTMLElement resizes.\nThis may be used to (re)scroll a cell into view.

\n" } ], - "html": "
onResize?: ((size: { height: number; width: number; }, prevSize: { height: number; width: number; }) => void)
", + "html": "
onResize?: (size: { height: number; width: number; }, prevSize: { height: number; width: number; }) => void
", "name": "onResize", "required": false }, @@ -162,7 +162,7 @@ "content": "

How many additional rows/columns to render outside of the visible area.\nThis can reduce visual flickering near the edges of a grid when scrolling.

\n" } ], - "html": "
overscanCount?: number = 3
", + "html": "
overscanCount?: number = \"3\"
", "name": "overscanCount", "required": false }, diff --git a/public/generated/js-docs/GridImperativeAPI.json b/public/generated/docs/GridImperativeAPI.json similarity index 100% rename from public/generated/js-docs/GridImperativeAPI.json rename to public/generated/docs/GridImperativeAPI.json diff --git a/public/generated/js-docs/List.json b/public/generated/docs/List.json similarity index 79% rename from public/generated/js-docs/List.json rename to public/generated/docs/List.json index 5ef73780..976ed4d9 100644 --- a/public/generated/js-docs/List.json +++ b/public/generated/docs/List.json @@ -43,7 +43,7 @@ "content": "

Default height of list for initial render.\nThis value is important for server rendering.

\n" } ], - "html": "
defaultHeight?: number = 0
", + "html": "
defaultHeight?: number = \"0\"
", "name": "defaultHeight", "required": false }, @@ -60,7 +60,7 @@ "intent": "primary" } ], - "html": "
listRef?: Ref<{ readonly element: HTMLDivElement | null; scrollToRow(config: { align?: \"auto\" | \"center\" | \"end\" | \"smart\" | \"start\"; behavior?: \"auto\" | \"instant\" | \"smooth\" | undefined; index: number; }): void; }> | undefined
", + "html": "
listRef?: Ref<{ readonly element: HTMLDivElement | null; scrollToRow(config: { align?: \"auto\" | \"center\" | \"end\" | \"smart\" | \"start\"; behavior?: \"auto\" | \"instant\" | \"smooth\"; index: number; }): void; }>
", "name": "listRef", "required": false }, @@ -70,7 +70,7 @@ "content": "

Callback notified when the List's outermost HTMLElement resizes.\nThis may be used to (re)scroll a row into view.

\n" } ], - "html": "
onResize?: ((size: { height: number; width: number; }, prevSize: { height: number; width: number; }) => void)
", + "html": "
onResize?: (size: { height: number; width: number; }, prevSize: { height: number; width: number; }) => void
", "name": "onResize", "required": false }, @@ -80,7 +80,7 @@ "content": "

Callback notified when the range of visible rows changes.

\n" } ], - "html": "
onRowsRendered?: ((visibleRows: { startIndex: number; stopIndex: number; }, allRows: { startIndex: number; stopIndex: number; }) => void)
", + "html": "
onRowsRendered?: (visibleRows: { startIndex: number; stopIndex: number; }, allRows: { startIndex: number; stopIndex: number; }) => void
", "name": "onRowsRendered", "required": false }, @@ -90,7 +90,7 @@ "content": "

How many additional rows to render outside of the visible area.\nThis can reduce visual flickering near the edges of a list when scrolling.

\n" } ], - "html": "
overscanCount?: number = 3
", + "html": "
overscanCount?: number = \"3\"
", "name": "overscanCount", "required": false }, diff --git a/public/generated/js-docs/ListImperativeAPI.json b/public/generated/docs/ListImperativeAPI.json similarity index 100% rename from public/generated/js-docs/ListImperativeAPI.json rename to public/generated/docs/ListImperativeAPI.json diff --git a/public/generated/examples/AriaRolesRoute.tsx b/public/generated/examples/AriaRolesRoute.tsx new file mode 100644 index 00000000..6a30ed53 --- /dev/null +++ b/public/generated/examples/AriaRolesRoute.tsx @@ -0,0 +1,3 @@ +{ + "html": "
import { Box, Code, ExternalLink, Header } from \"react-lib-tools\";
\n
import TableAriaAttributesMarkdown from \"../../../public/generated/examples/TableAriaAttributes.json\";
\n
import TableAriaOverridePropsMarkdown from \"../../../public/generated/examples/TableAriaOverrideProps.json\";
\n
 
\n
export default function AriaRolesRoute() {
\n
return (
\n
<Box direction=\"column\" gap={4}>
\n
<Header section=\"Tables\" title=\"ARIA roles\" />
\n
<div>
\n
The default ARIA role set by the <code>List</code> component is{\" \"}
\n
<ExternalLink href=\"https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Roles/list_role\">
\n
list
\n
</ExternalLink>{\" \"}
\n
, but the{\" \"}
\n
<ExternalLink href=\"https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Roles/table_role\">
\n
table
\n
</ExternalLink>{\" \"}
\n
role is more appropriate for tabular data.
\n
</div>
\n
<Code html={TableAriaAttributesMarkdown.html} />
\n
<div>
\n
The example on the previous page can be modified like so to assign the
\n
correct ARIA attributes:
\n
</div>
\n
<Code html={TableAriaOverridePropsMarkdown.html} />
\n
</Box>
\n
);
\n
}
" +} \ No newline at end of file diff --git a/public/generated/code-snippets/BasicRow.json b/public/generated/examples/BasicRow.json similarity index 100% rename from public/generated/code-snippets/BasicRow.json rename to public/generated/examples/BasicRow.json diff --git a/public/generated/code-snippets/CellComponent.json b/public/generated/examples/CellComponent.json similarity index 100% rename from public/generated/code-snippets/CellComponent.json rename to public/generated/examples/CellComponent.json diff --git a/public/generated/code-snippets/CellComponentAriaRoles.json b/public/generated/examples/CellComponentAriaRoles.json similarity index 100% rename from public/generated/code-snippets/CellComponentAriaRoles.json rename to public/generated/examples/CellComponentAriaRoles.json diff --git a/public/generated/examples/DynamicRowHeightsRoute.tsx b/public/generated/examples/DynamicRowHeightsRoute.tsx new file mode 100644 index 00000000..d2edc3c7 --- /dev/null +++ b/public/generated/examples/DynamicRowHeightsRoute.tsx @@ -0,0 +1,3 @@ +{ + "html": "
import {
\n
Block,
\n
Box,
\n
Callout,
\n
Code,
\n
Header,
\n
LoadingSpinner
\n
} from \"react-lib-tools\";
\n
import ListDynamicRowHeightsMarkdown from \"../../../public/generated/examples/ListDynamicRowHeights.json\";
\n
import ListRowDynamicRowHeightsMarkdown from \"../../../public/generated/examples/ListRowDynamicRowHeights.json\";
\n
import { ContinueLink } from \"../../components/ContinueLink\";
\n
import { Example } from \"./examples/ListDynamicRowHeights.example\";
\n
import { useLorem } from \"./hooks/useLorem\";
\n
 
\n
export default function DynamicRowHeightsRoute() {
\n
const lorem = useLorem();
\n
 
\n
return (
\n
<Box direction=\"column\" gap={4}>
\n
<Header section=\"Lists\" title=\"Dynamic row heights\" />
\n
<div>
\n
Sometimes the height of a row isn't known until it's been rendered.
\n
</div>
\n
<div>
\n
Here is an example list of lorem ipsum text of varying sizes. Each row
\n
can also be toggled collapsed/expanded by clicking on the \"+\"/\"-\"
\n
button.
\n
</div>
\n
<Block className=\"h-50\" data-focus-within=\"bold\">
\n
{!lorem.length && <LoadingSpinner />}
\n
<Example lorem={lorem} />
\n
</Block>
\n
<div>
\n
For this kind of list, react-window provides a helper hook called{\" \"}
\n
<code>useDynamicRowHeight</code>.
\n
</div>
\n
<Code html={ListDynamicRowHeightsMarkdown.html} />
\n
<div>
\n
In this case, rows can just render their content as they normally would
\n
and react-window will measure it for you.
\n
</div>
\n
<Code html={ListRowDynamicRowHeightsMarkdown.html} />
\n
<Callout intent=\"warning\">
\n
Dynamic row heights are not as efficient as predetermined sizes. It's
\n
recommended to provide your own height values if they can be determined
\n
ahead of time.
\n
</Callout>
\n
<ContinueLink to=\"/list/imperative-handle\" title=\"imperative methods\" />
\n
</Box>
\n
);
\n
}
" +} \ No newline at end of file diff --git a/public/generated/code-snippets/FixedHeightList.json b/public/generated/examples/FixedHeightList.json similarity index 100% rename from public/generated/code-snippets/FixedHeightList.json rename to public/generated/examples/FixedHeightList.json diff --git a/public/generated/code-snippets/FixedHeightRowComponent.json b/public/generated/examples/FixedHeightRowComponent.json similarity index 100% rename from public/generated/code-snippets/FixedHeightRowComponent.json rename to public/generated/examples/FixedHeightRowComponent.json diff --git a/public/generated/examples/FixedRowHeightsRoute.tsx b/public/generated/examples/FixedRowHeightsRoute.tsx new file mode 100644 index 00000000..efc5bbc5 --- /dev/null +++ b/public/generated/examples/FixedRowHeightsRoute.tsx @@ -0,0 +1,3 @@ +{ + "html": "
import {
\n
Block,
\n
Box,
\n
Callout,
\n
Code,
\n
ExternalLink,
\n
Header
\n
} from \"react-lib-tools\";
\n
import json from \"../../../public/data/names.json\";
\n
import FixedHeightListMarkdown from \"../../../public/generated/examples/FixedHeightList.json\";
\n
import FixedHeightRowComponentMarkdown from \"../../../public/generated/examples/FixedHeightRowComponent.json\";
\n
import { ContinueLink } from \"../../components/ContinueLink\";
\n
import { Example } from \"./examples/FixedHeightList.example\";
\n
 
\n
export default function FixedRowHeightsRoute() {
\n
return (
\n
<Box direction=\"column\" gap={4}>
\n
<Header section=\"Lists\" title=\"Fixed row heights\" />
\n
<div>
\n
The simplest type of list to render is one with fixed row heights.
\n
</div>
\n
<Block className=\"h-50\" data-focus-within=\"bold\">
\n
<Example names={json} />
\n
</Block>
\n
<div>
\n
To render this type of list, you need to specify how many rows it
\n
contains (<code>rowCount</code>), which component should render rows (
\n
<code>rowComponent</code>), and the height of each row (
\n
<code>rowHeight</code>):
\n
</div>
\n
<Code html={FixedHeightListMarkdown.html} />
\n
<div>
\n
The <code>rowProps</code> object can also be used to share between
\n
components. Values passed in <code>rowProps</code> will also be passed
\n
as props to the row component:
\n
</div>
\n
<Code html={FixedHeightRowComponentMarkdown.html} />
\n
<Callout intent=\"warning\">
\n
<Box direction=\"column\" gap={4}>
\n
<div>
\n
Lists require vertical space to render rows. Typically the{\" \"}
\n
<ExternalLink href=\"https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver\">
\n
ResizeObserver
\n
</ExternalLink>{\" \"}
\n
API is used to determine how much space there is available within
\n
the parent DOM element.
\n
</div>
\n
<div>
\n
If an explicit height is specified (in pixels) using the{\" \"}
\n
<code>style</code> prop, <code>ResizeObserver</code> will not be
\n
used.
\n
</div>
\n
</Box>
\n
</Callout>
\n
<ContinueLink
\n
to=\"/list/variable-row-height\"
\n
title=\"variable row heights\"
\n
/>
\n
</Box>
\n
);
\n
}
" +} \ No newline at end of file diff --git a/public/generated/code-snippets/FlexboxLayout.json b/public/generated/examples/FlexboxLayout.json similarity index 100% rename from public/generated/code-snippets/FlexboxLayout.json rename to public/generated/examples/FlexboxLayout.json diff --git a/public/generated/examples/GettingStartedRoute.tsx b/public/generated/examples/GettingStartedRoute.tsx new file mode 100644 index 00000000..7ff18507 --- /dev/null +++ b/public/generated/examples/GettingStartedRoute.tsx @@ -0,0 +1,3 @@ +{ + "html": "
import { Box, Callout, ExternalLink, Header } from \"react-lib-tools\";
\n
import { Link } from \"../components/Link\";
\n
 
\n
export default function GettingStartedRoute() {
\n
return (
\n
<Box direction=\"column\" gap={4}>
\n
<Header title=\"Getting started\" />
\n
<div>
\n
<strong>react-window</strong> is a component library that helps render
\n
large lists of data quickly and without the performance problems that
\n
often go along with rendering a lot of data. It's used in a lot of
\n
places, from{\" \"}
\n
<ExternalLink href=\"https://chromewebstore.google.com/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi?hl=en\">
\n
React DevTools
\n
</ExternalLink>{\" \"}
\n
to the{\" \"}
\n
<ExternalLink href=\"https://github.com/replayio/devtools\">
\n
Replay browser
\n
</ExternalLink>
\n
.
\n
</div>
\n
<div className=\"text-xl mt-4\">Installation</div>
\n
<div>Begin by installing the library from NPM:</div>
\n
<code className=\"grow text-xs md:text-sm block text-left whitespace-pre-wrap rounded-md p-3 bg-black text-white!\">
\n
npm install <span className=\"tok-keyword\">react-window</span>
\n
</code>
\n
<Callout intent=\"primary\">
\n
TypeScript definitions are included within the published{\" \"}
\n
<code>dist</code> folder.
\n
</Callout>
\n
<div className=\"text-xl mt-4\">Learn more</div>
\n
<div>
\n
If you've never used a library like this before, you may want to read
\n
the <Link to=\"/how-does-it-work\">how it works</Link> section first.
\n
</div>
\n
<div>
\n
Then when you're ready to render a list or a grid, pick a section below.
\n
</div>
\n
<ul className=\"pl-8\">
\n
<li className=\"list-disc\">
\n
<Link to=\"/list/fixed-row-height\">Lists</Link> (vertical scrolling)
\n
</li>
\n
<li className=\"list-disc\">
\n
<Link to=\"/grid/grid\">Grids</Link> (horizontal and vertical scrolling)
\n
</li>
\n
</ul>
\n
<div className=\"text-xl mt-4\">Support</div>
\n
If you like this project there are several ways to support it:
\n
<ul className=\"pl-8\">
\n
<li className=\"list-disc\">
\n
<ExternalLink href=\"https://github.com/sponsors/bvaughn/\">
\n
Become a GitHub sponsor
\n
</ExternalLink>
\n
</li>
\n
<li className=\"list-disc\">
\n
<ExternalLink href=\"https://opencollective.com/react-window#sponsor\">
\n
Become an Open Collective sponsor
\n
</ExternalLink>
\n
</li>
\n
<li className=\"list-disc\">
\n
or{\" \"}
\n
<ExternalLink href=\"http://givebrian.coffee/\">
\n
buy me a coffee
\n
</ExternalLink>
\n
</li>
\n
</ul>
\n
</Box>
\n
);
\n
}
" +} \ No newline at end of file diff --git a/public/generated/code-snippets/Grid.json b/public/generated/examples/Grid.json similarity index 100% rename from public/generated/code-snippets/Grid.json rename to public/generated/examples/Grid.json diff --git a/public/generated/code-snippets/GridAriaRoles.json b/public/generated/examples/GridAriaRoles.json similarity index 100% rename from public/generated/code-snippets/GridAriaRoles.json rename to public/generated/examples/GridAriaRoles.json diff --git a/public/generated/code-snippets/HorizontalList.json b/public/generated/examples/HorizontalList.json similarity index 100% rename from public/generated/code-snippets/HorizontalList.json rename to public/generated/examples/HorizontalList.json diff --git a/public/generated/code-snippets/HorizontalListCellRenderer.json b/public/generated/examples/HorizontalListCellRenderer.json similarity index 100% rename from public/generated/code-snippets/HorizontalListCellRenderer.json rename to public/generated/examples/HorizontalListCellRenderer.json diff --git a/public/generated/examples/HorizontalListsRoute.tsx b/public/generated/examples/HorizontalListsRoute.tsx new file mode 100644 index 00000000..466055dd --- /dev/null +++ b/public/generated/examples/HorizontalListsRoute.tsx @@ -0,0 +1,3 @@ +{ + "html": "
import { Block, Box, Code, Header, LoadingSpinner } from \"react-lib-tools\";
\n
import HorizontalListMarkdown from \"../../../public/generated/examples/HorizontalList.json\";
\n
import HorizontalListCellRendererMarkdown from \"../../../public/generated/examples/HorizontalListCellRenderer.json\";
\n
import { HorizontalList } from \"./examples/HorizontalList.example\";
\n
import { useEmails } from \"./hooks/useEmails\";
\n
 
\n
export default function HorizontalListsRoute() {
\n
const emails = useEmails();
\n
 
\n
return (
\n
<Box direction=\"column\" gap={4}>
\n
<Header section=\"Other\" title=\"Horizontal lists\" />
\n
<div>A horizontal list is just a grid with only one row.</div>
\n
<div>Here's an example horizontal list (grid) of emails:</div>
\n
<Block className=\"h-20\" data-focus-within=\"bold\">
\n
{!emails.length && <LoadingSpinner />}
\n
<HorizontalList emails={emails} />
\n
</Block>
\n
<div>Here's what the configuration for the grid above looks like:</div>
\n
<Code html={HorizontalListMarkdown.html} />
\n
<div>And here's the cell renderer:</div>
\n
<Code html={HorizontalListCellRendererMarkdown.html} />
\n
</Box>
\n
);
\n
}
" +} \ No newline at end of file diff --git a/public/generated/examples/HowDoesItWorkRoute.tsx b/public/generated/examples/HowDoesItWorkRoute.tsx new file mode 100644 index 00000000..4bdf1eb4 --- /dev/null +++ b/public/generated/examples/HowDoesItWorkRoute.tsx @@ -0,0 +1,3 @@ +{ + "html": "
import { ChevronRightIcon } from \"@heroicons/react/20/solid\";
\n
import type { PropsWithChildren } from \"react\";
\n
import { Box, Callout, Code, ExternalLink, Header, cn } from \"react-lib-tools\";
\n
import BasicRowMarkdown from \"../../public/generated/examples/BasicRow.json\";
\n
import { Link } from \"../components/Link\";
\n
import { getIntentClassNames } from \"../utils/getIntentClassNames\";
\n
 
\n
export default function HowDoesItWorkRoute() {
\n
return (
\n
<Box direction=\"column\" gap={4}>
\n
<Header title=\"How does it work?\" />
\n
<div>
\n
Libraries like this help to render a lot of items as efficiently as
\n
possible by limiting how many items are rendered at once.
\n
</div>
\n
<div>
\n
Below is an over-simplified illustration of a list with 6 rows. Only 2
\n
or 3 rows are rendered at a time because that is enough to fill the
\n
viewport. (The user can't see the other rows, so we don't <em>need</em>{\" \"}
\n
to render them).
\n
</div>
\n
<Box
\n
align=\"center\"
\n
className=\"pt-25\"
\n
direction=\"row\"
\n
gap={4}
\n
justify=\"center\"
\n
>
\n
<List className=\"\">
\n
<Row rendered>Row 1</Row>
\n
<Row rendered>Row 2</Row>
\n
<Row />
\n
<Row />
\n
<Row />
\n
<Row />
\n
<Viewport innerClassName=\"top-1\" outerClassName=\"top-0\" />
\n
</List>
\n
<ChevronRightIcon className=\"-mt-27 text-slate-500 w-8 h-8\" />
\n
<List className=\"-mt-20\">
\n
<Row />
\n
<Row rendered>Row 1</Row>
\n
<Row rendered>Row 2</Row>
\n
<Row rendered>Row 3</Row>
\n
<Row />
\n
<Row />
\n
<Viewport innerClassName=\"top-4\" outerClassName=\"top-10\" />
\n
</List>
\n
<ChevronRightIcon className=\"-mt-27 text-slate-500 w-8 h-8\" />
\n
<List className=\"-mt-55\">
\n
<Row />
\n
<Row />
\n
<Row />
\n
<Row />
\n
<Row rendered>Row 5</Row>
\n
<Row rendered>Row 6</Row>
\n
<Viewport innerClassName=\"top-11\" outerClassName=\"top-28\" />
\n
</List>
\n
</Box>
\n
<div>
\n
When a user scrolls the list, a different set of rows are rendered- but
\n
always only a few at a time.
\n
</div>
\n
<Callout intent=\"primary\">
\n
The illustration above shows unrendered rows as dimmed. In reality, they
\n
aren't there at all. The rows that do get rendered are positioned using
\n
CSS properties like{\" \"}
\n
<ExternalLink href=\"https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/top\">
\n
top
\n
</ExternalLink>{\" \"}
\n
to mimic other rows above them.
\n
</Callout>
\n
<div>
\n
To render one of these rows, all you need to provide is a component like
\n
the one below.
\n
</div>
\n
<Code html={BasicRowMarkdown.html} />
\n
<div>
\n
Continue to the <Link to=\"/list/fixed-row-height\">list examples</Link>{\" \"}
\n
to learn more.
\n
</div>
\n
</Box>
\n
);
\n
}
\n
 
\n
function List({
\n
children,
\n
className
\n
}: PropsWithChildren<{ className: string }>) {
\n
return (
\n
<div
\n
className={cn(\"relative flex flex-col gap-1 p-2 pr-6 w-30\", className)}
\n
>
\n
{children}
\n
</div>
\n
);
\n
}
\n
 
\n
function Row({
\n
children,
\n
rendered
\n
}: {
\n
children?: string;
\n
rendered?: boolean;
\n
}) {
\n
return (
\n
<div
\n
className={cn(
\n
\"h-6 p-2 flex items-center rounded text-xs whitespace-nowrap\",
\n
rendered
\n
? getIntentClassNames(\"primary\", true)
\n
: getIntentClassNames(\"none\", true),
\n
rendered ? \"\" : \"opacity-50\"
\n
)}
\n
>
\n
{children}
\n
</div>
\n
);
\n
}
\n
 
\n
function Viewport({
\n
innerClassName,
\n
outerClassName
\n
}: {
\n
innerClassName: string;
\n
outerClassName: string;
\n
}) {
\n
return (
\n
<div
\n
className={cn(
\n
\"rounded rounded-md border border-2 border-white absolute left-0 h-17 w-full\",
\n
outerClassName
\n
)}
\n
>
\n
<div className=\"absolute right-0 h-full w-4 bg-white/5\">
\n
<div
\n
className={cn(
\n
\"absolute right-1 h-4 w-2 rounded bg-white/50\",
\n
innerClassName
\n
)}
\n
></div>
\n
</div>
\n
</div>
\n
);
\n
}
" +} \ No newline at end of file diff --git a/public/generated/code-snippets/ImageRow.json b/public/generated/examples/ImageRow.json similarity index 100% rename from public/generated/code-snippets/ImageRow.json rename to public/generated/examples/ImageRow.json diff --git a/public/generated/code-snippets/Images.json b/public/generated/examples/Images.json similarity index 100% rename from public/generated/code-snippets/Images.json rename to public/generated/examples/Images.json diff --git a/public/generated/examples/ImagesRoute.tsx b/public/generated/examples/ImagesRoute.tsx new file mode 100644 index 00000000..854e87f6 --- /dev/null +++ b/public/generated/examples/ImagesRoute.tsx @@ -0,0 +1,3 @@ +{ + "html": "
import { Block, Box, Code, Header } from \"react-lib-tools\";
\n
import ImageRowMarkdown from \"../../../public/generated/examples/ImageRow.json\";
\n
import ImagesMarkdown from \"../../../public/generated/examples/Images.json\";
\n
import { Link } from \"../../components/Link\";
\n
import { ExampleWithImages } from \"./examples/Images.example\";
\n
 
\n
export default function ImagesRoute() {
\n
return (
\n
<Box direction=\"column\" gap={4}>
\n
<Header section=\"Other\" title=\"Images\" />
\n
<div>
\n
Images are example of{\" \"}
\n
<Link to=\"/list/dynamic-row-height\">dynamic row heights</Link>.
\n
</div>
\n
<Block className=\"h-150 max-h-[50vh]\" data-focus-within=\"bold\">
\n
<ExampleWithImages />
\n
</Block>
\n
<div>
\n
As with text, the <code>useDynamicRowHeight</code> hook can be used to
\n
measure images.
\n
</div>
\n
<Code html={ImagesMarkdown.html} />
\n
<div>
\n
A loading placeholder might be rendered until the image has been
\n
measured for the first time.
\n
</div>
\n
<Code html={ImageRowMarkdown.html} />
\n
</Box>
\n
);
\n
}
" +} \ No newline at end of file diff --git a/public/generated/examples/ImperativeApiRoute.tsx b/public/generated/examples/ImperativeApiRoute.tsx new file mode 100644 index 00000000..a9964e8a --- /dev/null +++ b/public/generated/examples/ImperativeApiRoute.tsx @@ -0,0 +1,3 @@ +{ + "html": "
import { Box, Code, ExternalLink, ImperativeHandle } from \"react-lib-tools\";
\n
import { html as useListCallbackRefHTML } from \"../../../public/generated/examples/useListCallbackRef.json\";
\n
import { html as useListRefHTML } from \"../../../public/generated/examples/useListRef.json\";
\n
import json from \"../../../public/generated/docs/ListImperativeAPI.json\";
\n
import type { ImperativeHandleMetadata } from \"../../types\";
\n
 
\n
export default function ListImperativeApiRoute() {
\n
return (
\n
<Box direction=\"column\" gap={4}>
\n
<ImperativeHandle
\n
json={json as ImperativeHandleMetadata}
\n
section=\"Imperative API\"
\n
/>
\n
<div className=\"text-lg font-bold\">Hooks</div>
\n
<div>
\n
The <code>useListRef</code> hook returns a{\" \"}
\n
<ExternalLink href=\"https://react.dev/reference/react/useRef\">
\n
mutable ref object
\n
</ExternalLink>
\n
.
\n
</div>
\n
<Code html={useListRefHTML} />
\n
<div>
\n
And the <code>useListCallbackRef</code> hook returns a{\" \"}
\n
<ExternalLink href=\"https://react.dev/reference/react-dom/components/common#ref-callback\">
\n
ref callback function
\n
</ExternalLink>
\n
. This is better when sharing the ref with another hook or component.
\n
</div>
\n
<Code html={useListCallbackRefHTML} />
\n
</Box>
\n
);
\n
}
" +} \ No newline at end of file diff --git a/public/generated/examples/ImperativeHandleRoute.tsx b/public/generated/examples/ImperativeHandleRoute.tsx new file mode 100644 index 00000000..996cbaf8 --- /dev/null +++ b/public/generated/examples/ImperativeHandleRoute.tsx @@ -0,0 +1,3 @@ +{ + "html": "
import { Box, Code, ExternalLink, ImperativeHandle } from \"react-lib-tools\";
\n
import { html as useGridCallbackRefHTML } from \"../../../public/generated/examples/useGridCallbackRef.json\";
\n
import { html as useGridRefHTML } from \"../../../public/generated/examples/useGridRef.json\";
\n
import json from \"../../../public/generated/docs/GridImperativeAPI.json\";
\n
import type { ImperativeHandleMetadata } from \"../../types\";
\n
 
\n
export default function GridImperativeHandleRoute() {
\n
return (
\n
<Box direction=\"column\" gap={4}>
\n
<ImperativeHandle
\n
json={json as ImperativeHandleMetadata}
\n
section=\"Imperative API\"
\n
/>
\n
<div className=\"text-lg font-bold\">Hooks</div>
\n
<div>
\n
The <code>useGridRef</code> hook returns a{\" \"}
\n
<ExternalLink href=\"https://react.dev/reference/react/useRef\">
\n
mutable ref object
\n
</ExternalLink>
\n
.
\n
</div>
\n
<Code html={useGridRefHTML} />
\n
<div>
\n
And the <code>useGridCallbackRef</code> hook returns a{\" \"}
\n
<ExternalLink href=\"https://react.dev/reference/react-dom/components/common#ref-callback\">
\n
ref callback function
\n
</ExternalLink>
\n
. This is better when sharing the ref with another hook or component.
\n
</div>
\n
<Code html={useGridCallbackRefHTML} />
\n
</Box>
\n
);
\n
}
" +} \ No newline at end of file diff --git a/public/generated/code-snippets/ListAriaRoles.json b/public/generated/examples/ListAriaRoles.json similarity index 100% rename from public/generated/code-snippets/ListAriaRoles.json rename to public/generated/examples/ListAriaRoles.json diff --git a/public/generated/code-snippets/ListDynamicRowHeights.json b/public/generated/examples/ListDynamicRowHeights.json similarity index 100% rename from public/generated/code-snippets/ListDynamicRowHeights.json rename to public/generated/examples/ListDynamicRowHeights.json diff --git a/public/generated/code-snippets/ListRowDynamicRowHeights.json b/public/generated/examples/ListRowDynamicRowHeights.json similarity index 100% rename from public/generated/code-snippets/ListRowDynamicRowHeights.json rename to public/generated/examples/ListRowDynamicRowHeights.json diff --git a/public/generated/code-snippets/ListVariableRowHeights.json b/public/generated/examples/ListVariableRowHeights.json similarity index 100% rename from public/generated/code-snippets/ListVariableRowHeights.json rename to public/generated/examples/ListVariableRowHeights.json diff --git a/public/generated/code-snippets/ListWithStickyRows.json b/public/generated/examples/ListWithStickyRows.json similarity index 100% rename from public/generated/code-snippets/ListWithStickyRows.json rename to public/generated/examples/ListWithStickyRows.json diff --git a/public/generated/examples/PageNotFound.tsx b/public/generated/examples/PageNotFound.tsx new file mode 100644 index 00000000..e5d025ce --- /dev/null +++ b/public/generated/examples/PageNotFound.tsx @@ -0,0 +1,3 @@ +{ + "html": "
import { Box, Callout, ExternalLink, Header } from \"react-lib-tools\";
\n
 
\n
export default function PageNotFound() {
\n
return (
\n
<Box direction=\"column\" gap={4}>
\n
<Header title=\"Page not found\" />
\n
<Callout intent=\"danger\">
\n
The URL you requested can't be found. If you think this is an error,{\" \"}
\n
<ExternalLink href=\"https://github.com/bvaughn/react-window/issues/new\">
\n
please file a GitHub issue
\n
</ExternalLink>
\n
.
\n
</Callout>
\n
</Box>
\n
);
\n
}
" +} \ No newline at end of file diff --git a/public/generated/examples/PlatformRequirementsRoute.tsx b/public/generated/examples/PlatformRequirementsRoute.tsx new file mode 100644 index 00000000..83bda7dd --- /dev/null +++ b/public/generated/examples/PlatformRequirementsRoute.tsx @@ -0,0 +1,3 @@ +{ + "html": "
import { Box, Callout, ExternalLink, Header } from \"react-lib-tools\";
\n
 
\n
export default function PlatformRequirementsRoute() {
\n
return (
\n
<Box direction=\"column\" gap={4}>
\n
<Header title=\"Requirements\" />
\n
<div>
\n
This library requires React{\" \"}
\n
<ExternalLink href=\"https://react.dev/blog/2022/03/29/react-v18\">
\n
version 18
\n
</ExternalLink>{\" \"}
\n
or newer.
\n
</div>
\n
<div>
\n
It also uses the{\" \"}
\n
<ExternalLink href=\"https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver\">
\n
ResizeObserver
\n
</ExternalLink>{\" \"}
\n
(or a polyfill) to calculate the available space for <code>List</code>{\" \"}
\n
and <code>Grid</code> components.
\n
</div>
\n
<Callout intent=\"primary\">
\n
<code>ResizeObserver</code> usage can be avoided if explicit pixel
\n
dimensions are specified using the <code>style</code> prop. (Percentage
\n
or EM/REM based dimensions do not count.)
\n
</Callout>
\n
</Box>
\n
);
\n
}
" +} \ No newline at end of file diff --git a/public/generated/examples/PropsRoute.tsx b/public/generated/examples/PropsRoute.tsx new file mode 100644 index 00000000..40820af8 --- /dev/null +++ b/public/generated/examples/PropsRoute.tsx @@ -0,0 +1,3 @@ +{ + "html": "
import { Box, ComponentProps } from \"react-lib-tools\";
\n
import json from \"../../../public/generated/docs/List.json\";
\n
import type { ComponentMetadata } from \"../../types\";
\n
 
\n
export default function ListPropsRoute() {
\n
return (
\n
<Box direction=\"column\" gap={4}>
\n
<ComponentProps json={json as ComponentMetadata} section=\"Lists\" />
\n
</Box>
\n
);
\n
}
" +} \ No newline at end of file diff --git a/public/generated/examples/RTLGridsRoute.tsx b/public/generated/examples/RTLGridsRoute.tsx new file mode 100644 index 00000000..b38965ba --- /dev/null +++ b/public/generated/examples/RTLGridsRoute.tsx @@ -0,0 +1,3 @@ +{ + "html": "
import {
\n
Block,
\n
Box,
\n
Code,
\n
ExternalLink,
\n
Header,
\n
LoadingSpinner
\n
} from \"react-lib-tools\";
\n
import RtlGridMarkdown from \"../../../public/generated/examples/RtlGrid.json\";
\n
import { RtlExample } from \"./examples/RtlGrid.example\";
\n
import { useContacts } from \"./hooks/useContacts\";
\n
 
\n
export default function RTLGridsRoute() {
\n
const contacts = useContacts();
\n
 
\n
return (
\n
<Box direction=\"column\" gap={4}>
\n
<Header section=\"Other\" title=\"Right to left content\" />
\n
<div>
\n
Grids can also display right to left languages (like Arabic). The grid
\n
components check the{\" \"}
\n
<ExternalLink href=\"https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/dir\">
\n
<code>dir</code> attribute
\n
</ExternalLink>{\" \"}
\n
to determine content directionality.
\n
</div>
\n
<div>
\n
Using the same data as from the previous example, here is a grid
\n
rendered right to left.
\n
</div>
\n
<Block className=\"h-50 overflow-auto\" data-focus-within=\"bold\">
\n
{!contacts.length && <LoadingSpinner />}
\n
<RtlExample contacts={contacts} />
\n
</Block>
\n
<Code html={RtlGridMarkdown.html} />
\n
</Box>
\n
);
\n
}
" +} \ No newline at end of file diff --git a/public/generated/examples/RenderingGridRoute.tsx b/public/generated/examples/RenderingGridRoute.tsx new file mode 100644 index 00000000..8adfe8ac --- /dev/null +++ b/public/generated/examples/RenderingGridRoute.tsx @@ -0,0 +1,3 @@ +{ + "html": "
import {
\n
Block,
\n
Box,
\n
Callout,
\n
Code,
\n
ExternalLink,
\n
Header,
\n
LoadingSpinner
\n
} from \"react-lib-tools\";
\n
import CellComponentMarkdown from \"../../../public/generated/examples/CellComponent.json\";
\n
import columnWidthMarkdown from \"../../../public/generated/examples/columnWidth.json\";
\n
import GridMarkdown from \"../../../public/generated/examples/Grid.json\";
\n
import { ContinueLink } from \"../../components/ContinueLink\";
\n
import { Example } from \"./examples/Grid.example\";
\n
import { useContacts } from \"./hooks/useContacts\";
\n
 
\n
export default function RenderingGridRoute() {
\n
const contacts = useContacts();
\n
 
\n
return (
\n
<Box direction=\"column\" gap={4}>
\n
<Header section=\"Grids\" title=\"Rendering a grid\" />
\n
<div>
\n
Use the <code>Grid</code> component to render data with many rows and
\n
columns:
\n
</div>
\n
<Block className=\"h-50 overflow-auto\" data-focus-within=\"bold\">
\n
{!contacts.length && <LoadingSpinner />}
\n
<Example contacts={contacts} />
\n
</Block>
\n
<div>
\n
Grids require you to specify the number of rows and columns as well as
\n
the width and height of each:
\n
</div>
\n
<Code html={GridMarkdown.html} />
\n
<div>
\n
Column widths and row heights can be either numbers or functions. In the
\n
example above, row height is fixed and column width is function that
\n
determines the width of the column based on the column index:
\n
</div>
\n
<Code html={columnWidthMarkdown.html} />
\n
<div>
\n
Lastly grids require a component to render cell, given a column and row
\n
index. As with lists, this component receives additional props specified
\n
as part of <code>cellProps</code>:
\n
</div>
\n
<Code html={CellComponentMarkdown.html} />
\n
<Callout intent=\"warning\">
\n
<Box direction=\"column\" gap={4}>
\n
<div>
\n
Grids require space to render cells. Typically the{\" \"}
\n
<ExternalLink href=\"https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver\">
\n
ResizeObserver
\n
</ExternalLink>{\" \"}
\n
API is used to determine how much space there is available within
\n
the parent DOM element.
\n
</div>
\n
<div>
\n
If an explicit width and height are specified (in pixels) using the{\" \"}
\n
<code>style</code> prop, <code>ResizeObserver</code> will not be
\n
used.
\n
</div>
\n
</Box>
\n
</Callout>
\n
<ContinueLink to=\"/list/imperative-handle\" title=\"imperative methods\" />
\n
</Box>
\n
);
\n
}
" +} \ No newline at end of file diff --git a/public/generated/code-snippets/RowComponentAriaRoles.json b/public/generated/examples/RowComponentAriaRoles.json similarity index 100% rename from public/generated/code-snippets/RowComponentAriaRoles.json rename to public/generated/examples/RowComponentAriaRoles.json diff --git a/public/generated/code-snippets/RtlGrid.json b/public/generated/examples/RtlGrid.json similarity index 100% rename from public/generated/code-snippets/RtlGrid.json rename to public/generated/examples/RtlGrid.json diff --git a/public/generated/examples/ScratchpadRoute.tsx b/public/generated/examples/ScratchpadRoute.tsx new file mode 100644 index 00000000..83816973 --- /dev/null +++ b/public/generated/examples/ScratchpadRoute.tsx @@ -0,0 +1,3 @@ +{ + "html": "
import { useCallback, useState, type ButtonHTMLAttributes } from \"react\";
\n
import {
\n
List,
\n
useDynamicRowHeight,
\n
useListCallbackRef,
\n
type RowComponentProps
\n
} from \"react-window\";
\n
 
\n
type Item = {
\n
children: Array<{ index: number }>;
\n
id: number;
\n
minHeight: number;
\n
name: string;
\n
};
\n
 
\n
export default function ScratchpadRoute() {
\n
const [key, setKey] = useState(0);
\n
const [items, setItems] = useState(createItems);
\n
const [expandedSet, setExpandedSet] = useState<Set<number>>(new Set());
\n
 
\n
const [list, setList] = useListCallbackRef();
\n
const rowHeight = useDynamicRowHeight({
\n
defaultRowHeight: 24
\n
});
\n
 
\n
const toggleExpand = useCallback(
\n
(id: number) => {
\n
const newSet = new Set(expandedSet);
\n
if (newSet.has(id)) {
\n
newSet.delete(id);
\n
} else {
\n
newSet.add(id);
\n
}
\n
setExpandedSet(newSet);
\n
},
\n
[expandedSet]
\n
);
\n
 
\n
return (
\n
<div className=\"flex flex-col gap-2\">
\n
<div className=\"flex items-center gap-2\">
\n
<Button
\n
onClick={() => {
\n
setItems(createItems());
\n
setExpandedSet(new Set());
\n
setKey(key + 1);
\n
}}
\n
>
\n
Reset
\n
</Button>
\n
<Button
\n
onClick={() => {
\n
list?.scrollToRow({
\n
align: \"end\",
\n
index: items.length - 1
\n
});
\n
}}
\n
>
\n
Scroll to Bottom
\n
</Button>
\n
</div>
\n
<List
\n
className=\"w-full h-100 border\"
\n
key={key}
\n
listRef={setList}
\n
rowComponent={Row}
\n
rowCount={items.length}
\n
rowHeight={rowHeight}
\n
rowProps={{
\n
expandedSet,
\n
items,
\n
toggleExpand
\n
}}
\n
/>
\n
</div>
\n
);
\n
}
\n
 
\n
function Row({
\n
expandedSet,
\n
items,
\n
toggleExpand,
\n
index,
\n
style
\n
}: RowComponentProps<{
\n
expandedSet: Set<number>;
\n
items: Item[];
\n
toggleExpand: (id: number) => void;
\n
}>) {
\n
const { children, minHeight, name } = items[index];
\n
 
\n
const isExpanded = expandedSet.has(index);
\n
 
\n
return (
\n
<div
\n
className=\"py-1 px-2 flex items-center gap-2\"
\n
style={{ ...style, minHeight }}
\n
>
\n
<Button disabled={!children.length} onClick={() => toggleExpand(index)}>
\n
{!children.length || isExpanded ? \"–\" : \"+\"}
\n
</Button>
\n
{name}
\n
{isExpanded && (
\n
<pre className=\"text-xs\">{JSON.stringify(children, null, 2)}</pre>
\n
)}
\n
</div>
\n
);
\n
}
\n
 
\n
function createItems() {
\n
const items: Item[] = [];
\n
 
\n
for (let index = 0; index < 500; ++index) {
\n
items.push({
\n
children: new Array(index % 5).fill(true).map((_, index) => ({
\n
index
\n
})),
\n
id: index,
\n
minHeight: 24 + 5 * (index % 3),
\n
name: `item ${index}`
\n
});
\n
}
\n
 
\n
return items;
\n
}
\n
 
\n
function Button({
\n
className,
\n
disabled,
\n
...rest
\n
}: ButtonHTMLAttributes<HTMLButtonElement>) {
\n
return (
\n
<button
\n
className={`rounded bg-gray-700 px-2 ${disabled ? \"opacity-35\" : \"cursor-pointer\"} ${className}`}
\n
disabled={disabled}
\n
{...rest}
\n
/>
\n
);
\n
}
" +} \ No newline at end of file diff --git a/public/generated/examples/ScrollToCellRoute.tsx b/public/generated/examples/ScrollToCellRoute.tsx new file mode 100644 index 00000000..fb8d07e4 --- /dev/null +++ b/public/generated/examples/ScrollToCellRoute.tsx @@ -0,0 +1,3 @@ +{ + "html": "
import { useMemo, useState } from \"react\";
\n
import {
\n
Block,
\n
Box,
\n
Button,
\n
Callout,
\n
Code,
\n
Header,
\n
LoadingSpinner,
\n
Select,
\n
type Option
\n
} from \"react-lib-tools\";
\n
import { Grid, useGridRef, type Align } from \"react-window\";
\n
import gridRefClickEventHandlerMarkdown from \"../../../public/generated/examples/gridRefClickEventHandler.json\";
\n
import useGridCallbackRefMarkdown from \"../../../public/generated/examples/useGridCallbackRef.json\";
\n
import useGridRefMarkdown from \"../../../public/generated/examples/useGridRef.json\";
\n
import useGridRefImportMarkdown from \"../../../public/generated/examples/useGridRefImport.json\";
\n
import { ContinueLink } from \"../../components/ContinueLink\";
\n
import { CellComponent } from \"./examples/CellComponent.example\";
\n
import { columnWidth } from \"./examples/columnWidth.example\";
\n
import type { Contact } from \"./examples/Grid.example\";
\n
import { COLUMN_KEYS } from \"./examples/shared\";
\n
import { useContacts } from \"./hooks/useContacts\";
\n
 
\n
const EMPTY_OPTION: Option<string> = {
\n
label: \"\",
\n
value: \"\"
\n
};
\n
 
\n
const ALIGNMENTS: Option<Align>[] = (
\n
[\"auto\", \"center\", \"end\", \"smart\", \"start\"] satisfies Align[]
\n
).map((value) => ({
\n
label: `align: ${value}`,
\n
value
\n
}));
\n
ALIGNMENTS.unshift(EMPTY_OPTION as Option<Align>);
\n
 
\n
const BEHAVIORS: Option<ScrollBehavior>[] = (
\n
[\"auto\", \"instant\", \"smooth\"] satisfies ScrollBehavior[]
\n
).map((value) => ({
\n
label: `behavior: ${value}`,
\n
value
\n
}));
\n
BEHAVIORS.unshift(EMPTY_OPTION as Option<ScrollBehavior>);
\n
 
\n
const COLUMNS: Option<string>[] = COLUMN_KEYS.map((key) => ({
\n
label: key,
\n
value: key
\n
})).sort((a, b) => a.label.localeCompare(b.label));
\n
 
\n
export default function ScrollToCellRoute() {
\n
const contacts = useContacts();
\n
 
\n
const titleOptions = useMemo<Option<string>[]>(() => {
\n
const options: Option<string>[] = [];
\n
if (contacts) {
\n
contacts
\n
.reduce((array, contact) => {
\n
if (!array.includes(contact.title)) {
\n
array.push(contact.title);
\n
}
\n
return array;
\n
}, new Array<string>())
\n
.sort()
\n
.forEach((title) => {
\n
options.push({
\n
label: title,
\n
value: title
\n
});
\n
});
\n
 
\n
options.unshift(EMPTY_OPTION);
\n
}
\n
 
\n
return options;
\n
}, [contacts]);
\n
 
\n
const [align, setAlign] = useState<Option<Align> | undefined>();
\n
const [behavior, setBehavior] = useState<
\n
Option<ScrollBehavior> | undefined
\n
>();
\n
const [column, setColumn] = useState<Option<string>>(EMPTY_OPTION);
\n
const [title, setTitle] = useState<Option<string>>(EMPTY_OPTION);
\n
 
\n
const gridRef = useGridRef(null);
\n
 
\n
const scrollToCell = () => {
\n
const grid = gridRef.current;
\n
if (grid) {
\n
const columnIndex = column?.value
\n
? COLUMN_KEYS.indexOf(column.value as keyof Contact)
\n
: undefined;
\n
 
\n
const rowIndex = title?.value
\n
? contacts.findIndex((row) => row.title === title.value)
\n
: undefined;
\n
 
\n
if (columnIndex !== undefined && rowIndex !== undefined) {
\n
grid.scrollToCell({
\n
behavior: behavior?.value,
\n
columnAlign: align?.value,
\n
columnIndex,
\n
rowAlign: align?.value,
\n
rowIndex
\n
});
\n
} else if (columnIndex !== undefined) {
\n
grid.scrollToColumn({
\n
align: align?.value,
\n
behavior: behavior?.value,
\n
index: columnIndex
\n
});
\n
} else if (rowIndex !== undefined) {
\n
grid.scrollToRow({
\n
align: align?.value,
\n
behavior: behavior?.value,
\n
index: rowIndex
\n
});
\n
}
\n
}
\n
};
\n
 
\n
return (
\n
<Box direction=\"column\" gap={4}>
\n
<Header section=\"Grids\" title=\"Imperative methods\" />
\n
<div>
\n
Grid provides an imperative API for responding to events. The
\n
recommended way to access this API is to use the exported ref hook:
\n
</div>
\n
<Code html={useGridRefImportMarkdown.html} />
\n
<div>Attach the ref during render:</div>
\n
<Code html={useGridRefMarkdown.html} />
\n
<div>And call API methods in an event handler:</div>
\n
<Code html={gridRefClickEventHandlerMarkdown.html} />
\n
<div>The form below uses the imperative API to scroll the list:</div>
\n
<Box direction=\"row\" gap={4}>
\n
<Select
\n
className=\"flex-1\"
\n
onChange={setAlign}
\n
options={ALIGNMENTS}
\n
placeholder=\"Align\"
\n
value={align}
\n
/>
\n
<Select
\n
className=\"flex-1\"
\n
onChange={setBehavior}
\n
options={BEHAVIORS}
\n
placeholder=\"Scroll behavior\"
\n
value={behavior}
\n
/>
\n
</Box>
\n
<Box direction=\"row\" gap={4}>
\n
<Select
\n
className=\"flex-1\"
\n
onChange={setTitle}
\n
options={titleOptions}
\n
placeholder=\"Job title\"
\n
value={title}
\n
/>
\n
<Select
\n
className=\"flex-1\"
\n
onChange={setColumn}
\n
options={COLUMNS}
\n
placeholder=\"Column\"
\n
value={column}
\n
/>
\n
<Button
\n
className=\"shrink-0\"
\n
disabled={!column.value && !title.value}
\n
onClick={scrollToCell}
\n
>
\n
Scroll
\n
</Button>
\n
</Box>
\n
<Block className=\"h-50\" data-focus-within=\"bold\">
\n
{!contacts.length && <LoadingSpinner />}
\n
<Grid
\n
cellComponent={CellComponent}
\n
cellProps={{ contacts }}
\n
columnCount={COLUMNS.length}
\n
columnWidth={columnWidth}
\n
gridRef={gridRef}
\n
rowCount={contacts.length}
\n
rowHeight={25}
\n
/>
\n
</Block>
\n
<div>
\n
The Grid API also provides <code>scrollToColumn</code> and{\" \"}
\n
<code>scrollToRow</code> methods for single-axis scrolling.
\n
</div>
\n
<Callout intent=\"primary\">
\n
<strong className=\"text-sky-300\">Note</strong> If you are passing the
\n
ref to another component or hook, use the ref callback function instead.
\n
</Callout>
\n
<Code html={useGridCallbackRefMarkdown.html} />
\n
<ContinueLink to=\"/grid/aria-roles\" title=\"ARIA roles\" />
\n
</Box>
\n
);
\n
}
" +} \ No newline at end of file diff --git a/public/generated/examples/ScrollToRowRoute.tsx b/public/generated/examples/ScrollToRowRoute.tsx new file mode 100644 index 00000000..1ec621d8 --- /dev/null +++ b/public/generated/examples/ScrollToRowRoute.tsx @@ -0,0 +1,3 @@ +{ + "html": "
import { useMemo, useState } from \"react\";
\n
import {
\n
Block,
\n
Box,
\n
Button,
\n
Callout,
\n
Code,
\n
Header,
\n
LoadingSpinner,
\n
Select,
\n
type Option
\n
} from \"react-lib-tools\";
\n
import { List, useListRef, type Align } from \"react-window\";
\n
import listRefClickEventHandlerMarkdown from \"../../../public/generated/examples/listRefClickEventHandler.json\";
\n
import useListCallbackRefMarkdown from \"../../../public/generated/examples/useListCallbackRef.json\";
\n
import useListRefMarkdown from \"../../../public/generated/examples/useListRef.json\";
\n
import useListRefImportMarkdown from \"../../../public/generated/examples/useListRefImport.json\";
\n
import { ContinueLink } from \"../../components/ContinueLink\";
\n
import { RowComponent } from \"./examples/ListVariableRowHeights.example\";
\n
import { rowHeight } from \"./examples/rowHeight.example\";
\n
import { useCitiesByState } from \"./hooks/useCitiesByState\";
\n
 
\n
const EMPTY_OPTION: Option<string> = {
\n
label: \"\",
\n
value: \"\"
\n
};
\n
 
\n
const ALIGNMENTS: Option<Align>[] = (
\n
[\"auto\", \"center\", \"end\", \"smart\", \"start\"] satisfies Align[]
\n
).map((value) => ({
\n
label: `align: ${value}`,
\n
value
\n
}));
\n
ALIGNMENTS.unshift(EMPTY_OPTION as Option<Align>);
\n
 
\n
const BEHAVIORS: Option<ScrollBehavior>[] = (
\n
[\"auto\", \"instant\", \"smooth\"] satisfies ScrollBehavior[]
\n
).map((value) => ({
\n
label: `behavior: ${value}`,
\n
value
\n
}));
\n
BEHAVIORS.unshift(EMPTY_OPTION as Option<ScrollBehavior>);
\n
 
\n
export default function ScrollToRowRoute() {
\n
const [align, setAlign] = useState<Option<Align> | undefined>();
\n
const [behavior, setBehavior] = useState<
\n
Option<ScrollBehavior> | undefined
\n
>();
\n
const [state, setState] = useState<Option<string>>(EMPTY_OPTION);
\n
 
\n
const citiesByState = useCitiesByState();
\n
 
\n
const stateOptions = useMemo<Option<string>[]>(() => {
\n
const options: Option<string>[] = citiesByState
\n
.filter((item) => item.type === \"state\")
\n
.map((item) => ({
\n
label: item.state,
\n
value: item.state
\n
}));
\n
options.unshift(EMPTY_OPTION);
\n
 
\n
return options;
\n
}, [citiesByState]);
\n
 
\n
const listRef = useListRef(null);
\n
 
\n
const scrollToRow = () => {
\n
const index = citiesByState.findIndex(
\n
(item) => item.type === \"state\" && item.state === state.value
\n
);
\n
listRef.current?.scrollToRow({
\n
align: align?.value,
\n
behavior: behavior?.value,
\n
index
\n
});
\n
};
\n
 
\n
return (
\n
<Box direction=\"column\" gap={4}>
\n
<Header section=\"Lists\" title=\"Imperative methods\" />
\n
<div>
\n
List provides an imperative API for responding to events. The
\n
recommended way to access this API is to use the exported ref hook:
\n
</div>
\n
<Code html={useListRefImportMarkdown.html} />
\n
<div>Attach the ref during render:</div>
\n
<Code html={useListRefMarkdown.html} />
\n
<div>And call API methods in an event handler:</div>
\n
<Code html={listRefClickEventHandlerMarkdown.html} />
\n
<div>The form below uses the imperative API to scroll the list:</div>
\n
<Box direction=\"row\" gap={4}>
\n
<Select
\n
className=\"flex-1\"
\n
onChange={setAlign}
\n
options={ALIGNMENTS}
\n
placeholder=\"Align\"
\n
value={align}
\n
/>
\n
<Select
\n
className=\"flex-1\"
\n
onChange={setBehavior}
\n
options={BEHAVIORS}
\n
placeholder=\"Scroll behavior\"
\n
value={behavior}
\n
/>
\n
</Box>
\n
<Box direction=\"row\" gap={4}>
\n
<Select
\n
className=\"flex-1\"
\n
onChange={setState}
\n
options={stateOptions}
\n
placeholder=\"State\"
\n
value={state}
\n
/>
\n
<Button
\n
className=\"shrink-0\"
\n
disabled={!state.value}
\n
onClick={scrollToRow}
\n
>
\n
Scroll
\n
</Button>
\n
</Box>
\n
<Block className=\"h-50\" data-focus-within=\"bold\">
\n
{!citiesByState.length && <LoadingSpinner />}
\n
<List
\n
listRef={listRef}
\n
rowComponent={RowComponent}
\n
rowCount={citiesByState.length}
\n
rowHeight={rowHeight}
\n
rowProps={{ items: citiesByState }}
\n
/>
\n
</Block>
\n
<Callout intent=\"primary\">
\n
<strong className=\"text-sky-300\">Note</strong> If you are passing the
\n
ref to another component or hook, use the ref callback function instead.
\n
</Callout>
\n
<Code html={useListCallbackRefMarkdown.html} />
\n
<ContinueLink to=\"/list/aria-roles\" title=\"ARIA roles\" />
\n
</Box>
\n
);
\n
}
" +} \ No newline at end of file diff --git a/public/generated/examples/StickyRowsRoute.tsx b/public/generated/examples/StickyRowsRoute.tsx new file mode 100644 index 00000000..5481ac0c --- /dev/null +++ b/public/generated/examples/StickyRowsRoute.tsx @@ -0,0 +1,3 @@ +{ + "html": "
import {
\n
Block,
\n
Box,
\n
Callout,
\n
Code,
\n
ExternalLink,
\n
Header
\n
} from \"react-lib-tools\";
\n
import ListWithStickyRowsMarkdown from \"../../../public/generated/examples/ListWithStickyRows.json\";
\n
import { Example } from \"./examples/ListWithStickyRows.example\";
\n
 
\n
export default function StickyRowsRoute() {
\n
return (
\n
<Box direction=\"column\" gap={4}>
\n
<Header section=\"Other\" title=\"Sticky rows\" />
\n
<div>
\n
If you want to render content on top of your list or grid, the safest
\n
method is to use a{\" \"}
\n
<ExternalLink href=\"https://react.dev/reference/react-dom/createPortal\">
\n
portal
\n
</ExternalLink>{\" \"}
\n
and render them directly into the parent document. This avoids potential
\n
clipping issues or z-index conflicts.
\n
</div>
\n
<div>
\n
For the specific case of \"sticky\" rows, you can render within the parent
\n
list or grid using the <code>children</code> prop:
\n
</div>
\n
<Block className=\"h-50\" data-focus-within=\"bold\">
\n
<Example />
\n
</Block>
\n
<div>The example above was created using code like this:</div>
\n
<Code html={ListWithStickyRowsMarkdown.html} />
\n
<Callout intent=\"warning\">
\n
<strong>Note</strong> the height of 0 in the example above prevents the
\n
sticky row from affecting the height of the parent list.
\n
</Callout>
\n
</Box>
\n
);
\n
}
" +} \ No newline at end of file diff --git a/public/generated/examples/SupportRoute.tsx b/public/generated/examples/SupportRoute.tsx new file mode 100644 index 00000000..d488e6a0 --- /dev/null +++ b/public/generated/examples/SupportRoute.tsx @@ -0,0 +1,3 @@ +{ + "html": "
import { Box, ExternalLink, Header } from \"react-lib-tools\";
\n
 
\n
export default function SupportRoute() {
\n
return (
\n
<Box direction=\"column\" gap={4}>
\n
<Header title=\"Support\" />
\n
<div>
\n
<ExternalLink href=\"https://github.com/bvaughn/react-window\">
\n
GitHub
\n
</ExternalLink>{\" \"}
\n
is the easiest place to look for help, but it's probably not the
\n
fastest. This project is maintained by a single developer so there is
\n
limited bandwidth for answering questions.
\n
</div>
\n
<div>
\n
I recommend asking questions on{\" \"}
\n
<ExternalLink href=\"https://stackoverflow.com/questions/tagged/react-window\">
\n
Stack Overflow
\n
</ExternalLink>{\" \"}
\n
or{\" \"}
\n
<ExternalLink href=\"https://reddit.com/r/reactjs\">Reddit</ExternalLink>{\" \"}
\n
to start with. Both sites have active communities who often respond
\n
quickly. If you don't find an answer there you can try opening a GitHub
\n
issue- but please take a moment first to see if your question has{\" \"}
\n
<ExternalLink href=\"https://github.com/bvaughn/react-window/issues?q=is%3Aissue%20state%3Aclosed\">
\n
has already been answered
\n
</ExternalLink>{\" \"}
\n
before opening a new one.
\n
</div>
\n
</Box>
\n
);
\n
}
" +} \ No newline at end of file diff --git a/public/generated/code-snippets/TableAriaAttributes.json b/public/generated/examples/TableAriaAttributes.json similarity index 100% rename from public/generated/code-snippets/TableAriaAttributes.json rename to public/generated/examples/TableAriaAttributes.json diff --git a/public/generated/code-snippets/TableAriaOverrideProps.json b/public/generated/examples/TableAriaOverrideProps.json similarity index 100% rename from public/generated/code-snippets/TableAriaOverrideProps.json rename to public/generated/examples/TableAriaOverrideProps.json diff --git a/public/generated/examples/TabularDataRoute.tsx b/public/generated/examples/TabularDataRoute.tsx new file mode 100644 index 00000000..bff7e896 --- /dev/null +++ b/public/generated/examples/TabularDataRoute.tsx @@ -0,0 +1,3 @@ +{ + "html": "
import {
\n
Block,
\n
Box,
\n
Callout,
\n
Code,
\n
Header,
\n
LoadingSpinner
\n
} from \"react-lib-tools\";
\n
import FlexboxLayoutMarkdown from \"../../../public/generated/examples/FlexboxLayout.json\";
\n
import { ContinueLink } from \"../../components/ContinueLink\";
\n
import { Link } from \"../../components/Link\";
\n
import { Example } from \"./examples/FlexboxLayout.example\";
\n
import { useAddresses } from \"./hooks/useAddresses\";
\n
 
\n
export default function TabularDataRoute() {
\n
const addresses = useAddresses();
\n
 
\n
return (
\n
<Box direction=\"column\" gap={4}>
\n
<Header section=\"Tables\" title=\"Rendering tabular data\" />
\n
<div>
\n
Many types of tabular data can be rendered using the list component.
\n
</div>
\n
<Block className=\"p-0!\" data-focus-within=\"bold\">
\n
{!addresses.length ? (
\n
<div className=\"p-2\">
\n
<LoadingSpinner />
\n
</div>
\n
) : (
\n
<Example addresses={addresses} />
\n
)}
\n
</Block>
\n
<div>
\n
The example above uses Flexbox layout to position columns and headers.
\n
</div>
\n
<Code html={FlexboxLayoutMarkdown.html} />
\n
<Callout intent=\"primary\">
\n
It may be more efficient to render data with many columns using the{\" \"}
\n
<Link to=\"/grid/grid\">Grid</Link> component.
\n
</Callout>
\n
<ContinueLink to=\"/list/tabular-data-aria-roles\" title=\"ARIA roles\" />
\n
</Box>
\n
);
\n
}
" +} \ No newline at end of file diff --git a/public/generated/examples/VariableRowHeightsRoute.tsx b/public/generated/examples/VariableRowHeightsRoute.tsx new file mode 100644 index 00000000..ce17bf7f --- /dev/null +++ b/public/generated/examples/VariableRowHeightsRoute.tsx @@ -0,0 +1,3 @@ +{ + "html": "
import { Block, Box, Code, Header, LoadingSpinner } from \"react-lib-tools\";
\n
import ListVariableRowHeightsMarkdown from \"../../../public/generated/examples/ListVariableRowHeights.json\";
\n
import { ContinueLink } from \"../../components/ContinueLink\";
\n
import { Example } from \"./examples/ListVariableRowHeights.example\";
\n
import { useCitiesByState } from \"./hooks/useCitiesByState\";
\n
 
\n
export default function VariableRowHeightsRoute() {
\n
const citiesByState = useCitiesByState();
\n
 
\n
return (
\n
<Box direction=\"column\" gap={4}>
\n
<Header section=\"Lists\" title=\"Variable row heights\" />
\n
<div>
\n
Lists with rows of different types may require different heights to
\n
render.
\n
</div>
\n
<div>
\n
Here is an example the most populous US postal codes, grouped by state.
\n
State rows \"headers\" are taller and are styled differently.
\n
</div>
\n
<Block className=\"h-50\" data-focus-within=\"bold\">
\n
{!citiesByState.length && <LoadingSpinner />}
\n
<Example items={citiesByState} />
\n
</Block>
\n
<div>
\n
This list requires a <code>rowHeight</code> function that tells it what
\n
height a row should be based on the type of data it contains.
\n
</div>
\n
<Code html={ListVariableRowHeightsMarkdown.html} />
\n
<ContinueLink to=\"/list/dynamic-row-height\" title=\"dynamic row heights\" />
\n
</Box>
\n
);
\n
}
" +} \ No newline at end of file diff --git a/public/generated/examples/VersionsRoute.tsx b/public/generated/examples/VersionsRoute.tsx new file mode 100644 index 00000000..426bc026 --- /dev/null +++ b/public/generated/examples/VersionsRoute.tsx @@ -0,0 +1,3 @@ +{ + "html": "
import { Box, ExternalLink, Header } from \"react-lib-tools\";
\n
import { Fragment } from \"react/jsx-runtime\";
\n
import GlobeIcon from \"../../public/svgs/globe.svg?react\";
\n
import TagsIcon from \"../../public/svgs/tags.svg?react\";
\n
 
\n
export default function VersionsRoute() {
\n
return (
\n
<Box direction=\"column\" gap={2}>
\n
<Header title=\"Previous releases\" />
\n
<div>Click below to view documentation for past releases.</div>
\n
{Object.entries(VERSIONS)
\n
.sort((a, b) => b[0].localeCompare(a[0]))
\n
.map(([major, minors]) => (
\n
<Fragment key={major}>
\n
<div className=\"text-lg mt-2\">Version {major}</div>
\n
<ul className=\"pl-8\">
\n
{Object.entries(minors)
\n
.sort((a, b) => b[0].localeCompare(a[0]))
\n
.map(([version, url]) => (
\n
<VersionLink key={version} url={url} version={version} />
\n
))}
\n
</ul>
\n
</Fragment>
\n
))}
\n
</Box>
\n
);
\n
}
\n
 
\n
const VERSIONS = {
\n
\"2\": {
\n
\"2.2.3\": \"https://react-window-9gegorjnr-brian-vaughns-projects.vercel.app\",
\n
\"2.1.2\": \"https://react-window-8cygyvomv-brian-vaughns-projects.vercel.app\",
\n
\"2.0.2\": \"https://react-window-btpcws98u-brian-vaughns-projects.vercel.app\"
\n
},
\n
\"1\": {
\n
\"1.8.11\":
\n
\"https://web.archive.org/web/20241225003549/https://react-window.vercel.app/\",
\n
\"1.7.2\": \"\",
\n
\"1.6.2\": \"\",
\n
\"1.5.2\": \"\",
\n
\"1.4.0\": \"\",
\n
\"1.3.1\": \"\",
\n
\"1.2.4\": \"\",
\n
\"1.1.2\": \"\",
\n
\"1.0.3\": \"\"
\n
}
\n
};
\n
 
\n
function VersionLink({ url, version }: { url: string; version: string }) {
\n
return (
\n
<li className=\"list-disc\">
\n
{version.split(\".\").slice(0, 2).join(\".\")}
\n
<span className=\"text-slate-400\">.x</span>
\n
{url && (
\n
<ExternalLink
\n
aria-label={`Documentation for version ${version}`}
\n
className=\"ml-4\"
\n
href={url}
\n
>
\n
<GlobeIcon className=\"inline w-4 h-4 text-teal-200\" /> documentation
\n
</ExternalLink>
\n
)}
\n
<ExternalLink
\n
aria-label={`GitHub tag for version ${version}`}
\n
className=\"ml-4\"
\n
href={`https://github.com/bvaughn/react-window/releases/tag/${version}`}
\n
>
\n
<TagsIcon className=\"inline w-4 h-4 text-teal-200\" /> source code
\n
</ExternalLink>
\n
</li>
\n
);
\n
}
" +} \ No newline at end of file diff --git a/public/generated/code-snippets/columnWidth.json b/public/generated/examples/columnWidth.json similarity index 100% rename from public/generated/code-snippets/columnWidth.json rename to public/generated/examples/columnWidth.json diff --git a/public/generated/code-snippets/gridRefClickEventHandler.json b/public/generated/examples/gridRefClickEventHandler.json similarity index 100% rename from public/generated/code-snippets/gridRefClickEventHandler.json rename to public/generated/examples/gridRefClickEventHandler.json diff --git a/public/generated/code-snippets/listRefClickEventHandler.json b/public/generated/examples/listRefClickEventHandler.json similarity index 100% rename from public/generated/code-snippets/listRefClickEventHandler.json rename to public/generated/examples/listRefClickEventHandler.json diff --git a/public/generated/code-snippets/rowHeight.json b/public/generated/examples/rowHeight.json similarity index 100% rename from public/generated/code-snippets/rowHeight.json rename to public/generated/examples/rowHeight.json diff --git a/public/generated/examples/shared.ts b/public/generated/examples/shared.ts new file mode 100644 index 00000000..56af7fcc --- /dev/null +++ b/public/generated/examples/shared.ts @@ -0,0 +1,3 @@ +{ + "html": "
import type { Contact } from \"./Grid.example\";
\n
 
\n
export const COLUMN_KEYS: (keyof Contact)[] = [
\n
\"title\",
\n
\"first_name\",
\n
\"last_name\",
\n
\"email\",
\n
\"gender\",
\n
\"address\",
\n
\"city\",
\n
\"state\",
\n
\"zip\",
\n
\"timezone\",
\n
\"company\",
\n
\"job_title\"
\n
];
\n
 
\n
export function indexToColumn(columnIndex: number): keyof Contact {
\n
return COLUMN_KEYS[columnIndex];
\n
}
" +} \ No newline at end of file diff --git a/public/generated/examples/useAddresses.ts b/public/generated/examples/useAddresses.ts new file mode 100644 index 00000000..a3f333af --- /dev/null +++ b/public/generated/examples/useAddresses.ts @@ -0,0 +1,3 @@ +{ + "html": "
import { useMemo } from \"react\";
\n
import json from \"../../../../public/data/addresses.json\";
\n
 
\n
type Address = (typeof json)[0];
\n
 
\n
export function useAddresses(): Address[] {
\n
return useMemo(() => {
\n
if (json) {
\n
return json.sort((a, b) => a.city.localeCompare(b.city));
\n
}
\n
 
\n
return [];
\n
}, []);
\n
}
" +} \ No newline at end of file diff --git a/public/generated/examples/useCitiesByState.ts b/public/generated/examples/useCitiesByState.ts new file mode 100644 index 00000000..722f2ba6 --- /dev/null +++ b/public/generated/examples/useCitiesByState.ts @@ -0,0 +1,3 @@ +{ + "html": "
import { useMemo } from \"react\";
\n
import json from \"../../../../public/data/addresses.json\";
\n
 
\n
type Item =
\n
| { type: \"state\"; state: string }
\n
| { type: \"zip\"; city: string; zip: string };
\n
 
\n
export function useCitiesByState(): Item[] {
\n
return useMemo(() => {
\n
const items: Item[] = [];
\n
 
\n
if (json) {
\n
let currentState: string = \"\";
\n
 
\n
json
\n
.sort((a, b) => {
\n
if (a.state !== b.state) {
\n
return a.state.localeCompare(b.state);
\n
} else if (a.city !== b.city) {
\n
return a.city.localeCompare(b.city);
\n
} else {
\n
return a.zip.localeCompare(b.zip);
\n
}
\n
})
\n
.forEach((address) => {
\n
if (address.state !== currentState) {
\n
currentState = address.state;
\n
 
\n
items.push({
\n
type: \"state\",
\n
state: address.state
\n
});
\n
}
\n
 
\n
items.push({
\n
type: \"zip\",
\n
city: address.city,
\n
zip: address.zip
\n
});
\n
});
\n
}
\n
 
\n
return items;
\n
}, []);
\n
}
" +} \ No newline at end of file diff --git a/public/generated/examples/useContacts.ts b/public/generated/examples/useContacts.ts new file mode 100644 index 00000000..db70a239 --- /dev/null +++ b/public/generated/examples/useContacts.ts @@ -0,0 +1,3 @@ +{ + "html": "
import { useMemo } from \"react\";
\n
import json from \"../../../../public/data/contacts.json\";
\n
 
\n
type Contact = (typeof json)[0];
\n
 
\n
export function useContacts(): Contact[] {
\n
return useMemo(() => {
\n
if (json) {
\n
return json.sort((a, b) => {
\n
if (a.title !== b.title) {
\n
return a.title.localeCompare(b.title);
\n
} else if (a.first_name !== b.first_name) {
\n
return a.first_name.localeCompare(b.first_name);
\n
} else {
\n
return a.last_name.localeCompare(b.last_name);
\n
}
\n
});
\n
}
\n
 
\n
return [];
\n
}, []);
\n
}
" +} \ No newline at end of file diff --git a/public/generated/examples/useEmails.ts b/public/generated/examples/useEmails.ts new file mode 100644 index 00000000..29d56e17 --- /dev/null +++ b/public/generated/examples/useEmails.ts @@ -0,0 +1,3 @@ +{ + "html": "
import { useMemo } from \"react\";
\n
import json from \"../../../../public/data/contacts.json\";
\n
 
\n
export function useEmails(): string[] {
\n
return useMemo(() => {
\n
if (json) {
\n
return json
\n
.map((contact) => contact.email)
\n
.sort((a, b) => a.localeCompare(b));
\n
}
\n
 
\n
return [];
\n
}, []);
\n
}
" +} \ No newline at end of file diff --git a/public/generated/code-snippets/useGridCallbackRef.json b/public/generated/examples/useGridCallbackRef.json similarity index 100% rename from public/generated/code-snippets/useGridCallbackRef.json rename to public/generated/examples/useGridCallbackRef.json diff --git a/public/generated/code-snippets/useGridRef.json b/public/generated/examples/useGridRef.json similarity index 100% rename from public/generated/code-snippets/useGridRef.json rename to public/generated/examples/useGridRef.json diff --git a/public/generated/code-snippets/useGridRefImport.json b/public/generated/examples/useGridRefImport.json similarity index 100% rename from public/generated/code-snippets/useGridRefImport.json rename to public/generated/examples/useGridRefImport.json diff --git a/public/generated/code-snippets/useListCallbackRef.json b/public/generated/examples/useListCallbackRef.json similarity index 100% rename from public/generated/code-snippets/useListCallbackRef.json rename to public/generated/examples/useListCallbackRef.json diff --git a/public/generated/code-snippets/useListRef.json b/public/generated/examples/useListRef.json similarity index 100% rename from public/generated/code-snippets/useListRef.json rename to public/generated/examples/useListRef.json diff --git a/public/generated/code-snippets/useListRefImport.json b/public/generated/examples/useListRefImport.json similarity index 100% rename from public/generated/code-snippets/useListRefImport.json rename to public/generated/examples/useListRefImport.json diff --git a/public/generated/examples/useLorem.ts b/public/generated/examples/useLorem.ts new file mode 100644 index 00000000..3cb92cd2 --- /dev/null +++ b/public/generated/examples/useLorem.ts @@ -0,0 +1,3 @@ +{ + "html": "
import json from \"../../../../public/data/lorem.json\";
\n
 
\n
export function useLorem() {
\n
return json;
\n
}
" +} \ No newline at end of file diff --git a/public/svgs/checkbox-checked.svg b/public/svgs/checkbox-checked.svg deleted file mode 100644 index 59a2e3fe..00000000 --- a/public/svgs/checkbox-checked.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/svgs/checkbox-indeterminate.svg b/public/svgs/checkbox-indeterminate.svg deleted file mode 100644 index f11c023f..00000000 --- a/public/svgs/checkbox-indeterminate.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/svgs/checkbox-unchecked.svg b/public/svgs/checkbox-unchecked.svg deleted file mode 100644 index c1d6a334..00000000 --- a/public/svgs/checkbox-unchecked.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/svgs/radio-checked.svg b/public/svgs/radio-checked.svg deleted file mode 100644 index 013b1bc8..00000000 --- a/public/svgs/radio-checked.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/svgs/radio-unchecked.svg b/public/svgs/radio-unchecked.svg deleted file mode 100644 index 903679ff..00000000 --- a/public/svgs/radio-unchecked.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/scripts/compile-code-snippets.ts b/scripts/compile-code-snippets.ts deleted file mode 100644 index 04717819..00000000 --- a/scripts/compile-code-snippets.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { readFile, writeFile } from "node:fs/promises"; -import { basename, join } from "node:path"; -import { initialize } from "./utils/initialize.ts"; -import { syntaxHighlight } from "./utils/syntax-highlight.ts"; -import { trimExcludedText } from "./utils/code-snippets/trimExcludedText.ts"; - -async function run() { - const { files, outputDir } = await initialize({ - fileExtensions: [".html", ".ts", ".tsx"], - inputPath: ["src", "routes"], - outputDirName: "code-snippets" - }); - - const exampleFiles = files.filter((file) => file.includes(".example.")); - - for (const file of exampleFiles) { - const buffer = await readFile(file); - - let rawText = buffer.toString(); - - { - // Remove special comments and directives before syntax highlighting - rawText = trimExcludedText(rawText); - - rawText = rawText - .split("\n") - .filter( - (line) => - !line.includes("prettier-ignore") && - !line.includes("eslint-disable-next-line") && - !line.includes("@ts-expect-error") && - !line.includes("// hidden") - ) - .join("\n"); - } - - let html; - if (file.endsWith(".html")) { - html = await syntaxHighlight(rawText, "HTML"); - } else if (file.endsWith(".js") || file.endsWith(".jsx")) { - html = await syntaxHighlight( - rawText, - file.endsWith("jsx") ? "JSX" : "JS" - ); - } else if (file.endsWith(".ts") || file.endsWith(".tsx")) { - html = await syntaxHighlight( - rawText, - file.endsWith("tsx") ? "TSX" : "TS" - ); - } else { - throw Error(`Unsupported file type: ${file}`); - } - - const fileName = basename(file); - - const outputFile = join( - outputDir, - fileName.replace(/\.example\..+$/, ".json") - ); - - console.debug("Writing to", outputFile); - - await writeFile(outputFile, JSON.stringify({ html }, null, 2)); - } -} - -run(); diff --git a/scripts/compile-docs.ts b/scripts/compile-docs.ts index 57af3783..cddc0f0e 100644 --- a/scripts/compile-docs.ts +++ b/scripts/compile-docs.ts @@ -1,16 +1,6 @@ -import { compileComponents } from "./utils/docs/compileComponents.ts"; -import { compileImperativeHandles } from "./utils/docs/compileImperativeHandles.ts"; +import { compileDocs } from "react-lib-tools/scripts/compile-docs.ts"; -async function run() { - await compileComponents({ - componentNames: ["grid/Grid.tsx", "list/List.tsx"], - outputDirName: "js-docs" - }); - - await compileImperativeHandles({ - names: ["GridImperativeAPI", "ListImperativeAPI"], - outputDirName: "js-docs" - }); -} - -run(); +await compileDocs({ + componentNames: ["grid/Grid.tsx", "list/List.tsx"], + imperativeHandleNames: ["GridImperativeAPI", "ListImperativeAPI"] +}); diff --git a/scripts/compile-examples.ts b/scripts/compile-examples.ts new file mode 100644 index 00000000..0b0c76d4 --- /dev/null +++ b/scripts/compile-examples.ts @@ -0,0 +1,3 @@ +import { compileExamples } from "react-lib-tools/scripts/compile-examples.ts"; + +await compileExamples(); diff --git a/scripts/utils/code-snippets/trimExcludedText.ts b/scripts/utils/code-snippets/trimExcludedText.ts deleted file mode 100644 index 7869932f..00000000 --- a/scripts/utils/code-snippets/trimExcludedText.ts +++ /dev/null @@ -1,13 +0,0 @@ -export function trimExcludedText(rawText: string) { - { - const pieces = rawText.split("// "); - rawText = pieces[pieces.length - 1].trim(); - } - - { - const pieces = rawText.split("// "); - rawText = pieces[0].trim(); - } - - return rawText; -} diff --git a/scripts/utils/docs/compileComponent.ts b/scripts/utils/docs/compileComponent.ts deleted file mode 100644 index f33ddf9a..00000000 --- a/scripts/utils/docs/compileComponent.ts +++ /dev/null @@ -1,111 +0,0 @@ -import { writeFile } from "node:fs/promises"; -import { join, relative } from "node:path"; -import { cwd } from "node:process"; -import { type FileParser, type PropItem } from "react-docgen-typescript"; -import { assert } from "../../../lib/utils/assert.ts"; -import type { ComponentMetadata } from "../../../src/types.ts"; -import { getPropTypeText } from "./getPropTypeText.ts"; -import { parseDescription } from "./parseDescription.ts"; -import { syntaxHighlight } from "../syntax-highlight.ts"; -import { propsToTable } from "./propsToTable.ts"; - -const TOKEN_TO_REPLACE = "TOKEN_TO_REPLACE"; - -export async function compileComponent({ - filePath, - outputDir, - parser -}: { - filePath: string; - outputDir: string; - parser: FileParser; -}) { - const parsed = parser.parse(filePath); - assert( - parsed.length === 1, - `Expected 1 parsed component but found ${parsed.length}` - ); - - const component = parsed[0]; - - // Convert to local paths - component.filePath = relative(cwd(), filePath); - - // Filter inherited HTML attributes - for (const key in component.props) { - const prop = component.props[key]; - if ( - prop.declarations?.filter( - (declaration) => !declaration.fileName.includes("node_modules") - ).length === 0 - ) { - delete component.props[key]; - } - } - - // Generate syntax highlighted HTML for prop types - { - const componentMetadata: ComponentMetadata = { - description: await parseDescription(component.description), - filePath: component.filePath, - name: component.displayName, - props: {} - }; - - for (const name in component.props) { - const prop = component.props[name]; - - let textToFormat = getPropTypeText(prop); - - if (prop.defaultValue?.value) { - textToFormat = `${textToFormat} = ${prop.defaultValue.value}`; - } - - // Format with a placeholder token so we can replace it with a formatted string - textToFormat = `${TOKEN_TO_REPLACE}${prop.required ? "" : "?"}: ${textToFormat}`; - - try { - let html = await syntaxHighlight(textToFormat, "TS"); - html = html.replace( - TOKEN_TO_REPLACE, - `${name}` - ); - - componentMetadata.props[name] = { - description: await parseDescription(prop.description), - html, - name, - required: prop.required - }; - } catch (error) { - console.error(error); - } - } - - const outputFile = join(outputDir, `${component.displayName}.json`); - - console.debug("Writing to", outputFile); - - await writeFile(outputFile, JSON.stringify(componentMetadata, null, 2)); - } - - // Generate markdown for prop types - const requiredProps: PropItem[] = []; - const optionalProps: PropItem[] = []; - - for (const propName in component.props) { - const prop = component.props[propName]; - if (prop.required) { - requiredProps.push(prop); - } else { - optionalProps.push(prop); - } - } - - return { - componentName: component.displayName, - description: component.description, - optionalPropsTable: await propsToTable(optionalProps), - requiredPropsTable: await propsToTable(requiredProps) - }; -} diff --git a/scripts/utils/docs/compileComponents.ts b/scripts/utils/docs/compileComponents.ts deleted file mode 100644 index da09c426..00000000 --- a/scripts/utils/docs/compileComponents.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { readFile, writeFile } from "node:fs/promises"; -import { join } from "node:path"; -import { cwd } from "node:process"; -import { withCustomConfig } from "react-docgen-typescript"; -import { initialize } from "../initialize.ts"; -import { compileComponent } from "./compileComponent.ts"; -import { insertPropsMarkdown } from "./insertPropsMarkdown.ts"; - -export async function compileComponents({ - componentNames, - outputDirName -}: { - componentNames: string[]; - outputDirName: string; -}) { - const parser = withCustomConfig("./tsconfig.json", { - savePropValueAsString: true, - shouldExtractLiteralValuesFromEnum: true, - shouldExtractValuesFromUnion: true, - shouldRemoveUndefinedFromOptional: true - }); - - const { files, outputDir } = await initialize({ - fileExtensions: [".ts", ".tsx"], - fileFilter: (file) => - componentNames.some((componentName) => file.endsWith(componentName)), - inputPath: ["lib", "components"], - outputDirName - }); - - const markdownPath = join(cwd(), "README.md"); - - let markdown = await readFile(markdownPath, { encoding: "utf-8" }); - - await Promise.all( - files.map((filePath) => - compileComponent({ - filePath, - outputDir, - parser - }).then( - ({ - componentName, - description, - optionalPropsTable, - requiredPropsTable - }) => { - markdown = insertPropsMarkdown({ - componentMarkdown: description, - componentName, - markdown, - section: "description" - }); - - markdown = insertPropsMarkdown({ - componentMarkdown: requiredPropsTable, - componentName, - markdown, - section: "required-props" - }); - - markdown = insertPropsMarkdown({ - componentMarkdown: optionalPropsTable, - componentName, - markdown, - section: "optional-props" - }); - } - ) - ) - ); - - await writeFile(markdownPath, markdown); -} diff --git a/scripts/utils/docs/compileImperativeHandle.ts b/scripts/utils/docs/compileImperativeHandle.ts deleted file mode 100644 index 3e218e8a..00000000 --- a/scripts/utils/docs/compileImperativeHandle.ts +++ /dev/null @@ -1,48 +0,0 @@ -import type { InterfaceNode } from "@ts-ast-parser/core"; -import assert from "node:assert"; -import { writeFile } from "node:fs/promises"; -import { join } from "path"; -import type { ImperativeHandleMetadata } from "../../../src/types"; -import { syntaxHighlight } from "../syntax-highlight.ts"; -import { parseDescription } from "./parseDescription.ts"; - -export async function compileImperativeHandle({ - filePath, - interfaceNode, - outputDir -}: { - filePath: string; - interfaceNode: InterfaceNode; - outputDir: string; -}) { - const name = interfaceNode.getName(); - - const json: ImperativeHandleMetadata = { - description: await parseDescription( - "" + interfaceNode.getJSDoc().getTag("description")?.text - ), - filePath, - methods: [], - name - }; - - const methods = interfaceNode.getMethods(); - for (const method of methods) { - const jsDoc = method.getJSDoc(); - assert(jsDoc); - - json.methods.push({ - description: await parseDescription( - "" + jsDoc.getTag("description")?.text - ), - html: await syntaxHighlight(method.getTSNode().getText(), "TS"), - name: method.getName() - }); - } - - const outputFile = join(outputDir, `${name}.json`); - - console.debug("Writing to", outputFile); - - await writeFile(outputFile, JSON.stringify(json, null, 2)); -} diff --git a/scripts/utils/docs/compileImperativeHandles.ts b/scripts/utils/docs/compileImperativeHandles.ts deleted file mode 100644 index 57fbdbe3..00000000 --- a/scripts/utils/docs/compileImperativeHandles.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { parseFromProject, type InterfaceNode } from "@ts-ast-parser/core"; -import tsConfig from "../../../tsconfig.json" with { type: "json" }; -import { compileImperativeHandle } from "./compileImperativeHandle.ts"; -import { join } from "node:path"; -import { cwd } from "node:process"; - -export async function compileImperativeHandles({ - names, - outputDirName -}: { - names: string[]; - outputDirName: string; -}) { - const outputDir = join(cwd(), "public", "generated", outputDirName); - - const result = await parseFromProject(tsConfig); - const reflectedModules = result.project?.getModules() ?? []; - - const nodes: { - filePath: string; - node: InterfaceNode; - }[] = []; - - names.forEach((name) => { - reflectedModules.forEach((reflectedModule) => { - const node = reflectedModule.getDeclarationByName(name); - if (node) { - nodes.push({ - filePath: reflectedModule.getSourcePath(), - node: node as unknown as InterfaceNode - }); - } - }); - }); - - await Promise.all( - nodes.map(({ filePath, node }) => - compileImperativeHandle({ - filePath, - interfaceNode: node, - outputDir - }) - ) - ); -} diff --git a/scripts/utils/docs/formatDescriptionText.ts b/scripts/utils/docs/formatDescriptionText.ts deleted file mode 100644 index 9e047cf1..00000000 --- a/scripts/utils/docs/formatDescriptionText.ts +++ /dev/null @@ -1,11 +0,0 @@ -import Markdown from "markdown-it"; - -let processor: Markdown | undefined = undefined; - -export function formatDescriptionText(text: string) { - if (processor === undefined) { - processor = new Markdown(); - } - - return processor.render(text); -} diff --git a/scripts/utils/docs/getPropTypeText.ts b/scripts/utils/docs/getPropTypeText.ts deleted file mode 100644 index f888e61a..00000000 --- a/scripts/utils/docs/getPropTypeText.ts +++ /dev/null @@ -1,28 +0,0 @@ -import type { PropItem } from "react-docgen-typescript"; - -export function getPropTypeText(prop: PropItem) { - let textToFormat = prop.type.raw; - if (!textToFormat && prop.type.name.includes(":")) { - // Edge case where some prop types aren't registered as containing raw TS - textToFormat = prop.type.name; - - // List/Grid and rowComponent/cellComponent are annotated with a return type of ReactElement instead of ReactNode - // As a result of this change the generated docs are significantly less readable, so tidy them up here - // See github.com/bvaughn/react-resizable-panels/issues/875 - textToFormat = textToFormat.replace( - "ReactElement>", - "ReactNode" - ); - } - - if (!textToFormat) { - textToFormat = `${prop.type.name}`; - - const match = textToFormat.match(/ExcludeForbiddenKeys<([^>]+)>/); - if (match) { - textToFormat = match[1]; - } - } - - return textToFormat; -} diff --git a/scripts/utils/docs/insertPropsMarkdown.ts b/scripts/utils/docs/insertPropsMarkdown.ts deleted file mode 100644 index 2cce2adb..00000000 --- a/scripts/utils/docs/insertPropsMarkdown.ts +++ /dev/null @@ -1,29 +0,0 @@ -export function insertPropsMarkdown({ - componentMarkdown, - componentName, - markdown, - section -}: { - componentMarkdown: string; - componentName: string; - markdown: string; - section: string; -}) { - const flag = `${componentName}:${section}`; - const startToken = ``; - const stopToken = ``; - - const startIndex = markdown.indexOf(startToken) + startToken.length; - const stopIndex = markdown.indexOf(stopToken); - if (startIndex < 0 || stopIndex < 0) { - throw Error("Parsing README failed"); - } - - return ( - markdown.substring(0, startIndex) + - "\n" + - (componentMarkdown || "None") + - "\n" + - markdown.substring(stopIndex) - ); -} diff --git a/scripts/utils/docs/parseDescription.ts b/scripts/utils/docs/parseDescription.ts deleted file mode 100644 index 22a4300e..00000000 --- a/scripts/utils/docs/parseDescription.ts +++ /dev/null @@ -1,59 +0,0 @@ -import type { Intent, Section } from "../../../src/types.ts"; -import { formatDescriptionText } from "./formatDescriptionText.ts"; -import { syntaxHighlight, type Language } from "../syntax-highlight.ts"; - -export async function parseDescription(rawText: string) { - const sections: Section[] = []; - - // Paper over differences between "@ts-ast-parser/core" and "react-docgen-typescript" - let text = rawText; - Object.keys(INTENT_FLAGS).forEach((flag) => { - text = text - .split(`\n${flag}`) - .join(`\n\n${flag}`) - .replaceAll("\n\n\n", "\n\n"); - }); - - for (const chunk of text.split("\n\n")) { - let content = ""; - let intent: Intent | undefined = undefined; - - if (chunk.startsWith("```")) { - const match = chunk.match(/^```([a-z]+)/)!; - const language = match[1].toUpperCase() as Language; - - content = await syntaxHighlight( - chunk.substring(language.length + 3, chunk.length - 3).trim(), - language - ); - } else { - content = formatDescriptionText(chunk.trim()); - - for (const char in INTENT_FLAGS) { - if (content.startsWith(`

${char} `)) { - intent = INTENT_FLAGS[char as keyof typeof INTENT_FLAGS] as Intent; - content = content.replace(`

${char} `, "

"); - } - } - } - - // Strip TSDoc comments - content = content.replace(/\n@param.+/, ""); - content = content.replace(/\n@return.+/, ""); - - sections.push({ - content, - intent - }); - } - - return sections; -} - -const INTENT_FLAGS = { - "❌": "danger", - "NOTE:": "none", - ℹ️: "primary", - "✅": "success", - "⚠️": "warning" -}; diff --git a/scripts/utils/docs/propsToTable.ts b/scripts/utils/docs/propsToTable.ts deleted file mode 100644 index 7cd96ae6..00000000 --- a/scripts/utils/docs/propsToTable.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { marked } from "marked"; -import type { PropItem } from "react-docgen-typescript"; -import { getPropTypeText } from "./getPropTypeText.ts"; - -const TABLE_TAG_START = ` - - - - - - - - `; - -const PROP_ROW = ` - - - - `; - -const TABLE_TAG_STOP = ` - -
NameDescription
[[name]][[description]]
-`; - -export async function propsToTable(props: PropItem[]) { - const htmlStrings = []; - - if (props.length > 0) { - htmlStrings.push(TABLE_TAG_START); - - for (const prop of props) { - const type = getPropTypeText(prop); - - const description = await marked(prop.description); - - htmlStrings.push( - PROP_ROW.replace("[[name]]", prop.name) - .replace("[[type]]", type) - .replace("[[description]]", description) - ); - } - - htmlStrings.push(TABLE_TAG_STOP); - } - - return htmlStrings.join(""); -} diff --git a/scripts/utils/getFilesWithExtensions.ts b/scripts/utils/getFilesWithExtensions.ts deleted file mode 100644 index 8e9dd33c..00000000 --- a/scripts/utils/getFilesWithExtensions.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { readdir } from "fs/promises"; -import { extname, join } from "node:path"; - -export async function getFilesWithExtensions( - directory: string, - extensions: string[], - filter?: (path: string) => boolean -) { - const files: string[] = []; - - const entries = await readdir(directory, { withFileTypes: true }); - - for (const entry of entries) { - const fullPath = join(directory, entry.name); - - if (entry.isDirectory()) { - files.push( - ...(await getFilesWithExtensions(fullPath, extensions, filter)) - ); - } else if (entry.isFile()) { - const fileExtension = extname(entry.name); - if (extensions.includes(fileExtension)) { - if (typeof filter !== "function" || filter(fullPath)) { - files.push(fullPath); - } - } - } - } - - return files; -} diff --git a/scripts/utils/initialize.ts b/scripts/utils/initialize.ts deleted file mode 100644 index ffdfba62..00000000 --- a/scripts/utils/initialize.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { mkdir } from "node:fs/promises"; -import { join } from "node:path"; -import { cwd } from "node:process"; -import { getFilesWithExtensions } from "./getFilesWithExtensions.ts"; -import { rmFilesWithExtensions } from "./rmFilesWithExtensions.ts"; - -export async function initialize({ - fileExtensions, - fileFilter, - inputPath, - outputDirName -}: { - fileExtensions: string[]; - fileFilter?: (path: string) => boolean; - inputPath: string[]; - outputDirName: string; -}) { - const inputDir = join(cwd(), ...inputPath); - const outputDir = join(cwd(), "public", "generated", outputDirName); - await mkdir(outputDir, { recursive: true }); - await rmFilesWithExtensions(outputDir, [".json"]); - - const files = await getFilesWithExtensions( - inputDir, - fileExtensions, - fileFilter - ); - - return { - files, - inputDir, - outputDir - }; -} diff --git a/scripts/utils/rmFilesWithExtensions.ts b/scripts/utils/rmFilesWithExtensions.ts deleted file mode 100644 index 29eb30b8..00000000 --- a/scripts/utils/rmFilesWithExtensions.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { rm } from "fs/promises"; -import { getFilesWithExtensions } from "./getFilesWithExtensions.ts"; - -export async function rmFilesWithExtensions( - directory: string, - extensions: string[] -) { - const files = await getFilesWithExtensions(directory, extensions); - - for (const file of files) { - await rm(file); - } -} diff --git a/scripts/utils/syntax-highlight.ts b/scripts/utils/syntax-highlight.ts deleted file mode 100644 index abd8926f..00000000 --- a/scripts/utils/syntax-highlight.ts +++ /dev/null @@ -1,255 +0,0 @@ -import { htmlLanguage } from "@codemirror/lang-html"; -import { - jsxLanguage, - tsxLanguage, - typescriptLanguage -} from "@codemirror/lang-javascript"; -import { ensureSyntaxTree, LRLanguage } from "@codemirror/language"; -import { EditorState } from "@codemirror/state"; -import { classHighlighter, highlightTree } from "@lezer/highlight"; -import { type BuiltInParserName } from "prettier"; - -type TokenType = string; -type Token = { - columnIndex: number; - type: TokenType | null; - value: string; -}; - -export type Language = "HTML" | "JS" | "JSX" | "TS" | "TSX"; - -type State = { - parsedTokens: Token[]; - rawString: string; -}; - -export const DEFAULT_MAX_CHARACTERS = 500000; -export const DEFAULT_MAX_TIME = 5000; - -export async function syntaxHighlight(code: string, language: Language) { - let extension: LRLanguage; - let prettierParser: BuiltInParserName; - switch (language) { - case "HTML": { - extension = htmlLanguage.configure({ dialect: "selfClosing" }); - prettierParser = "html"; - break; - } - case "JS": - case "JSX": { - extension = jsxLanguage; - prettierParser = "babel"; - break; - } - case "TS": { - extension = typescriptLanguage; - prettierParser = "typescript"; - break; - } - case "TSX": { - extension = tsxLanguage; - prettierParser = "typescript"; - break; - } - } - if (!extension) { - console.error("Unsupported language %o", language); - } - - const tokens = await parser(code, extension, prettierParser); - - return tokens.map(parsedTokensToHtml).join("\n"); -} - -async function parser( - code: string, - languageExtension: LRLanguage, - - // @ts-expect-error TS6133 - // eslint-disable-next-line @typescript-eslint/no-unused-vars - prettierParser: BuiltInParserName -) { - const parsedTokens: Token[][] = []; - const currentLineState: State = { - parsedTokens: [], - rawString: "" - }; - - // The logic below to trim code sections only works with "\n" - code = code.replace(/\r\n?|\n|\u2028|\u2029/g, "\n"); - if (code.length > DEFAULT_MAX_CHARACTERS) { - let index = DEFAULT_MAX_CHARACTERS - 1; - while (index > 0 && code.charAt(index) !== "\n") { - index--; - } - if (index === 0) { - while (index < code.length && code.charAt(index) !== "\n") { - index++; - } - } - code = code.slice(0, index + 1); - } - - // In the future I may want to (re)format code examples for different screen widths - // code = await format(code, { - // parser: prettierParser, - // plugins: ["prettier-plugin-tailwindcss"], - // printWidth: 100, - // proseWrap: "always", - // semi: true - // }); - - const state = EditorState.create({ - doc: code, - extensions: [languageExtension] - }); - - const tree = ensureSyntaxTree( - state, - DEFAULT_MAX_CHARACTERS, - DEFAULT_MAX_TIME - ); - - if (tree === null) { - return []; - } - - let characterIndex = 0; - let parsedCharacterIndex = 0; - highlightTree( - tree, - classHighlighter, - (from: number, to: number, className: string) => { - if (from > characterIndex) { - // No style applied to the token between position and from. - // This typically indicates white space or newline characters. - processSection( - currentLineState, - parsedTokens, - code.slice(characterIndex, from), - "" - ); - } - processSection( - currentLineState, - parsedTokens, - code.slice(from, to), - className - ); - characterIndex = to; - } - ); - - const maxPosition = code.length; - - if (characterIndex < maxPosition) { - // No style applied on the trailing text. - // This typically indicates white space or newline characters. - processSection( - currentLineState, - parsedTokens, - code.slice(characterIndex, maxPosition), - "" - ); - } - if (currentLineState.parsedTokens.length) { - parsedTokens.push(currentLineState.parsedTokens); - } - - parsedCharacterIndex += characterIndex + 1; - - // Anything that's left should de-opt to plain text. - if (parsedCharacterIndex < code.length) { - let nextIndex = code.indexOf("\n", parsedCharacterIndex); - let parsedLineTokens = []; - while (true) { - const line = - nextIndex >= 0 - ? code.substring(parsedCharacterIndex, nextIndex) - : code.substring(parsedCharacterIndex); - parsedLineTokens.push({ - columnIndex: 0, - type: null, - value: line - }); - if (nextIndex >= 0) { - parsedTokens.push(parsedLineTokens); - parsedLineTokens = []; - } else if (nextIndex === -1) { - break; - } - parsedCharacterIndex = nextIndex + 1; - nextIndex = code.indexOf("\n", parsedCharacterIndex); - } - if (parsedLineTokens.length) { - parsedTokens.push(parsedLineTokens); - } - } - - return parsedTokens; -} - -function processSection( - currentLineState: State, - parsedTokens: Token[][], - section: string, - className: string -) { - // Remove "tok-" prefix; - const tokenType = - className === null || className === void 0 - ? null - : className.substring(4) || null; - - let index = 0; - let nextIndex = section.indexOf("\n"); - while (true) { - const substring = - nextIndex >= 0 - ? section.substring(index, nextIndex) - : section.substring(index); - const token: Token = { - columnIndex: currentLineState.rawString.length, - type: tokenType, - value: substring - }; - currentLineState.parsedTokens.push(token); - currentLineState.rawString += substring; - if (nextIndex === -1) { - break; - } - if (nextIndex >= 0) { - parsedTokens.push(currentLineState.parsedTokens); - currentLineState.parsedTokens = []; - currentLineState.rawString = ""; - } - index = nextIndex + 1; - nextIndex = section.indexOf("\n", index); - } -} - -function parsedTokensToHtml(tokens: Token[]) { - const htmlStrings = tokens.map((token) => { - const className = token.type ? `tok-${token.type}` : ""; - const escapedValue = escapeHtmlEntities(token.value); - - return `${escapedValue}`; - }); - - // Edge case to avoid empty line - let htmlString = htmlStrings.join(""); - if (tokens.length <= 1) { - if (!tokens[0].value) { - htmlString = " "; - } - } - - return `

${htmlString}
`; -} - -function escapeHtmlEntities(rawString: string) { - return rawString.replace( - /[\u00A0-\u9999<>&]/g, - (substring) => "&#" + substring.charCodeAt(0) + ";" - ); -} diff --git a/src/App.tsx b/src/App.tsx index fac23e6f..c88f6fc2 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,126 +1,54 @@ -import { Bars4Icon, XMarkIcon } from "@heroicons/react/20/solid"; -import { BrowserRouter, Route, Routes } from "react-router-dom"; -import GitHubIcon from "../public/svgs/github.svg?react"; -import NpmHubIcon from "../public/svgs/npm.svg?react"; -import { Box } from "./components/Box"; -import { ErrorBoundary } from "./components/ErrorBoundary"; -import { ExternalLink } from "./components/ExternalLink"; -import { Link } from "./components/Link"; -import { RouteChangeHandler } from "./components/RouteChangeHandler"; -import { useNavStore } from "./hooks/useNavStore"; -import { Nav } from "./nav/Nav"; +import { AppRoot, NavLink, NavSection } from "react-lib-tools"; import { routes } from "./routes"; -import { cn } from "./utils/cn"; -import TagsIcon from "../public/svgs/tags.svg?react"; export default function App() { - const { toggle, visible } = useNavStore(); - return ( - - - -
- - - -
- render everything -
-
- - - - - - - - - - - - -
-
-
-
-
-
- - {Object.entries(routes).map(([path, Component]) => ( - - - - } - key={path} - path={path} - /> - ))} - -
-
-
-
-
+ +
+ Getting started + How does it work? +
+ + Fixed row heights + + Variable row heights + + + Dynamic row heights + + Scroll to row + ARIA roles + List props + Imperative handle + + + Tabular data + ARIA roles + + + Rendering a grid + Scroll to cells + ARIA roles + Grid props + Imperative handle + + + Right to left content + Horizontal lists + Images + Sticky rows + +
+ Requirements + Support +
+ + } + packageDescription="render everything" + packageName="react-window" + routes={routes} + /> ); } diff --git a/src/components/Block.tsx b/src/components/Block.tsx deleted file mode 100644 index 679a0e58..00000000 --- a/src/components/Block.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import type { HTMLAttributes, PropsWithChildren } from "react"; -import { ErrorBoundary } from "./ErrorBoundary"; - -export function Block({ - children, - className, - ...rest -}: PropsWithChildren & { className?: string }>) { - return ( - -
- {children} -
-
- ); -} diff --git a/src/components/Box.tsx b/src/components/Box.tsx deleted file mode 100644 index ed49e71a..00000000 --- a/src/components/Box.tsx +++ /dev/null @@ -1,75 +0,0 @@ -import type { CSSProperties, HTMLAttributes } from "react"; -import { cn } from "../utils/cn"; - -export function Box({ - align, - children, - className, - direction, - gap = 0, - grow, - justify, - shrink, - style, - wrap, - ...rest -}: HTMLAttributes & { - align?: "center" | "end" | "start" | "stretch"; - className?: string; - direction: "column" | "row"; - gap?: 0 | 1 | 2 | 3 | 4; - grow?: 0 | 1; - justify?: "around" | "between" | "center" | "end" | "start" | "stretch"; - shrink?: 0 | 1; - style?: CSSProperties; - wrap?: boolean; -}) { - return ( -
- {children} -
- ); -} diff --git a/src/components/Button.tsx b/src/components/Button.tsx deleted file mode 100644 index d7ec8188..00000000 --- a/src/components/Button.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import type { HTMLAttributes, PropsWithChildren } from "react"; -import type { Intent } from "../types"; -import { Button as HeadlessButton } from "@headlessui/react"; -import { cn } from "../utils/cn"; - -export function Button({ - children, - className, - disabled, - intent = "none", - ...rest -}: PropsWithChildren< - HTMLAttributes & { - className?: string; - disabled?: boolean; - intent?: Intent; - } ->) { - return ( - - {children} - - ); -} - -function getClassNames(intent: Intent, disabled: boolean) { - switch (intent) { - case "danger": { - return cn("bg-red-400 text-red-800 focus:text-black", { - "hover:bg-red-500 hover:text-red-950 focus:text-black": !disabled - }); - } - case "none": { - return cn("bg-emerald-400 text-emerald-800 focus:text-black", { - "hover:bg-emerald-500 hover:text-emerald-950 focus:text-black": - !disabled - }); - } - case "success": - case "primary": { - return cn("bg-sky-400 text-sky-800 focus:text-black", { - "hover:bg-sky-500 hover:text-sky-950 focus:text-black": !disabled - }); - } - case "warning": { - return cn("bg-amber-400 text-amber-800 focus:text-black", { - "hover:bg-amber-500 hover:text-amber-950 focus:text-black": !disabled - }); - } - } -} diff --git a/src/components/Callout.tsx b/src/components/Callout.tsx deleted file mode 100644 index 0da55998..00000000 --- a/src/components/Callout.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import { - CheckCircleIcon, - ExclamationTriangleIcon, - InformationCircleIcon -} from "@heroicons/react/20/solid"; -import type { HTMLAttributes, PropsWithChildren } from "react"; -import type { Intent } from "../types"; -import { getIntentClassNames } from "../utils/getIntentClassNames"; - -export function Callout({ - children, - className, - html = false, - inline = false, - intent = "none", - minimal, - ...rest -}: PropsWithChildren< - HTMLAttributes & { - className?: string; - html?: boolean; - inline?: boolean; - intent?: Intent; - minimal?: boolean; - } ->) { - let Icon = ExclamationTriangleIcon; - switch (intent) { - case "none": - case "primary": { - Icon = InformationCircleIcon; - break; - } - case "success": { - Icon = CheckCircleIcon; - break; - } - } - - return ( -
-
- - {html ? ( -
- ) : ( -
{children}
- )} -
-
- ); -} diff --git a/src/components/Checkbox.tsx b/src/components/Checkbox.tsx deleted file mode 100644 index 8e1cfc78..00000000 --- a/src/components/Checkbox.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import { - type FunctionComponent, - type HTMLAttributes, - type PropsWithChildren, - type SVGProps -} from "react"; -import CheckedIcon from "../../public/svgs/checkbox-checked.svg?react"; -import IndeterminateIcon from "../../public/svgs/checkbox-indeterminate.svg?react"; -import UncheckedIcon from "../../public/svgs/checkbox-unchecked.svg?react"; - -export function Checkbox({ - checked, - children, - className, - indeterminate, - onChange, - ...rest -}: PropsWithChildren< - Omit, "defaultChecked" | "onChange"> & { - checked: boolean; - className?: string; - indeterminate?: boolean; - onChange: (value: boolean) => void; - } ->) { - let IconElement: FunctionComponent>; - let iconClassName: string; - if (indeterminate) { - IconElement = IndeterminateIcon; - iconClassName = "fill-white"; - } else if (checked) { - IconElement = CheckedIcon; - iconClassName = "fill-blue-600"; - } else { - IconElement = UncheckedIcon; - iconClassName = "fill-slate-600"; - } - - return ( - - ); -} diff --git a/src/components/DocsSection.tsx b/src/components/DocsSection.tsx deleted file mode 100644 index c6e3ca6d..00000000 --- a/src/components/DocsSection.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import type { Section } from "../types"; -import { Box } from "./Box"; -import { Callout } from "./Callout"; - -export function DocsSection({ - className, - sections -}: { - className?: string; - sections: Section[]; -}) { - return ( - - {sections.map(({ content, intent }, index) => { - if (intent) { - return ( - - {content} - - ); - } - - return ( -
", '
    ') - }} - >
- ); - })} -
- ); -} diff --git a/src/components/ErrorBoundary.tsx b/src/components/ErrorBoundary.tsx deleted file mode 100644 index d2200c89..00000000 --- a/src/components/ErrorBoundary.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import type { PropsWithChildren } from "react"; -import { - ErrorBoundary as ErrorBoundaryExternal, - type FallbackProps -} from "react-error-boundary"; -import { Callout } from "./Callout"; -import { Box } from "./Box"; -import { Button } from "./Button"; - -export function ErrorBoundary({ children }: PropsWithChildren) { - return ( - - {children} - - ); -} - -function FallbackComponent({ error, resetErrorBoundary }: FallbackProps) { - return ( - - -
Something went wrong!
-
-          {error.message}
-        
- -
-
- ); -} diff --git a/src/components/ExternalLink.tsx b/src/components/ExternalLink.tsx deleted file mode 100644 index dbec8a3f..00000000 --- a/src/components/ExternalLink.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import type { AnchorHTMLAttributes } from "react"; - -export function ExternalLink({ - children, - className, - href, - target = "_blank", - ...rest -}: AnchorHTMLAttributes) { - return ( - - {children} - - ); -} diff --git a/src/components/Header.tsx b/src/components/Header.tsx deleted file mode 100644 index e1beb5ca..00000000 --- a/src/components/Header.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import { ChevronRightIcon } from "@heroicons/react/20/solid"; -import { Box } from "./Box"; -import { useEffect } from "react"; - -export function Header({ - section, - title -}: { - section?: string; - title: string; -}) { - useEffect(() => { - const originalTitle = document.title; - - document.title = `react-window: ${section ? `${section}: ${title}` : title}`; - - return () => { - document.title = originalTitle; - }; - }); - - return ( - - {section && ( - <> -
{section}
- - - )} -
{title}
-
- ); -} diff --git a/src/components/Input.tsx b/src/components/Input.tsx deleted file mode 100644 index cc961522..00000000 --- a/src/components/Input.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import type { InputHTMLAttributes, PropsWithChildren } from "react"; - -export function Input({ - children, - className, - onChange, - value, - ...rest -}: PropsWithChildren< - Omit, "onChange"> & { - className?: string; - onChange: (value: Type) => void; - value: Type; - } ->) { - return ( - { - onChange(event.currentTarget.value as Type); - }} - value={value} - {...rest} - > - {children} - - ); -} diff --git a/src/components/Link.tsx b/src/components/Link.tsx index cb4f650f..855ab655 100644 --- a/src/components/Link.tsx +++ b/src/components/Link.tsx @@ -1,6 +1,6 @@ import type { HTMLAttributes } from "react"; +import { Link as ExternalLink } from "react-lib-tools"; import type { Path } from "../routes"; -import { TransitionLink } from "./TransitionLink"; export function Link({ to, @@ -8,5 +8,5 @@ export function Link({ }: HTMLAttributes & { to: Path; }) { - return ; + return ; } diff --git a/src/components/LoadingSpinner.tsx b/src/components/LoadingSpinner.tsx deleted file mode 100644 index 6f61bd2e..00000000 --- a/src/components/LoadingSpinner.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import { ArrowPathIcon } from "@heroicons/react/20/solid"; -import { Box } from "./Box"; - -export function LoadingSpinner() { - return ( - - - Loading... - - ); -} diff --git a/src/components/Radio.tsx b/src/components/Radio.tsx deleted file mode 100644 index cd256530..00000000 --- a/src/components/Radio.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import { - type FunctionComponent, - type HTMLAttributes, - type PropsWithChildren, - type SVGProps -} from "react"; -import CheckedIcon from "../../public/svgs/radio-checked.svg?react"; -import UncheckedIcon from "../../public/svgs/radio-unchecked.svg?react"; - -export function Radio({ - checked, - children, - className, - name, - onChange, - value, - ...rest -}: PropsWithChildren< - Omit, "defaultChecked" | "onChange"> & { - checked: boolean; - name: string; - onChange: (value: Value) => void; - value: Value; - } ->) { - let IconElement: FunctionComponent>; - let iconClassName: string; - if (checked) { - IconElement = CheckedIcon; - iconClassName = "fill-blue-600"; - } else { - IconElement = UncheckedIcon; - iconClassName = "fill-slate-600"; - } - - return ( - - ); -} diff --git a/src/components/RouteChangeHandler.tsx b/src/components/RouteChangeHandler.tsx deleted file mode 100644 index 5b7098e8..00000000 --- a/src/components/RouteChangeHandler.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { useLocation } from "react-router-dom"; -import { useIsomorphicLayoutEffect } from "../../lib/hooks/useIsomorphicLayoutEffect"; -import { useNavStore } from "../hooks/useNavStore"; - -export function RouteChangeHandler() { - const { hide } = useNavStore(); - - const { pathname } = useLocation(); - - useIsomorphicLayoutEffect(() => { - hide(); - - const main = document.body.querySelector("[data-main-scrollable]"); - if (main) { - const timeout = setTimeout(() => { - main.scrollTo(0, 0); - }, 1); - - return () => { - clearTimeout(timeout); - }; - } - }, [pathname]); - - return null; -} diff --git a/src/components/Select.tsx b/src/components/Select.tsx deleted file mode 100644 index 0c3349f8..00000000 --- a/src/components/Select.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import { - Listbox, - ListboxButton, - ListboxOption, - ListboxOptions, - Transition -} from "@headlessui/react"; -import { ChevronUpDownIcon } from "@heroicons/react/20/solid"; -import { Fragment } from "react"; -import { cn } from "../utils/cn"; - -export type Option = { - label: string; - value: Value; -}; - -export function Select({ - className, - defaultValue, - onChange, - options, - placeholder = "", - value -}: { - className?: string; - defaultValue?: Option | undefined; - onChange: (value: Option) => void; - options: Option[]; - placeholder?: string; - value: Option | undefined; -}) { - return ( - -
- - {value?.label ? ( - {value.label} - ) : ( - {placeholder} - )} - - - - - - {options.map((option, index) => ( - - {option.label} - - ))} - - -
-
- ); -} diff --git a/src/components/TransitionLink.tsx b/src/components/TransitionLink.tsx deleted file mode 100644 index 75daaecc..00000000 --- a/src/components/TransitionLink.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import { useTransition, type HTMLAttributes, type ReactNode } from "react"; -import { useMatch, useNavigate } from "react-router-dom"; -import type { Path } from "../routes"; - -type RenderFunction = (params: { - isActive: boolean; - isPending: boolean; -}) => ReactNode; - -export function TransitionLink({ - children, - onClick, - to, - ...rest -}: Omit, "children"> & { - children?: ReactNode | RenderFunction; - to: Path; -}) { - const isActive = !!useMatch(to); - const [isPending, startTransition] = useTransition(); - const navigate = useNavigate(); - - return ( - { - onClick?.(event); - - startTransition(() => { - navigate(to); - }); - }} - {...rest} - /> - ); -} diff --git a/src/components/code/Code.tsx b/src/components/code/Code.tsx deleted file mode 100644 index 8dfe7349..00000000 --- a/src/components/code/Code.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { cn } from "../../utils/cn"; -import "./code-mirror.css"; - -export function Code({ - className = "", - html -}: { - className?: string; - html: string; -}) { - return ( -
- -
- ); -} diff --git a/src/components/code/code-mirror.css b/src/components/code/code-mirror.css deleted file mode 100644 index 2a44b5ba..00000000 --- a/src/components/code/code-mirror.css +++ /dev/null @@ -1,36 +0,0 @@ -.tok-comment { - color: var(--color-slate-500); -} -.tok-definition { -} -.tok-local { -} -.tok-keyword { - color: var(--color-pink-400); -} -.tok-meta { -} -.tok-number { -} -.tok-operator { - color: var(--color-slate-400); -} -.tok-propertyName { - color: var(--color-sky-300); -} -.tok-punctuation { - color: var(--color-slate-400); -} -.tok-string { - color: var(--color-teal-300); -} -.tok-string2 { - color: var(--color-sky-300); -} -.tok-typeName { - color: var(--color-pink-400); -} -.tok-variableName:not(.tok-definition) { -} -.tok-variableName2 { -} diff --git a/src/components/code/types.ts b/src/components/code/types.ts deleted file mode 100644 index 37c8821a..00000000 --- a/src/components/code/types.ts +++ /dev/null @@ -1 +0,0 @@ -export type Language = "JSX" | "TypeScript" | "TSX"; diff --git a/src/components/handles/ImperativeHandle.tsx b/src/components/handles/ImperativeHandle.tsx deleted file mode 100644 index 463ca7dc..00000000 --- a/src/components/handles/ImperativeHandle.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import { ArrowTopRightOnSquareIcon } from "@heroicons/react/20/solid"; -import { repository } from "../../../package.json"; -import type { ImperativeHandleMetadata } from "../../types"; -import { Box } from "../Box"; -import { DocsSection } from "../DocsSection"; -import { ExternalLink } from "../ExternalLink"; -import { Header } from "../Header"; -import { ImperativeHandleMethod } from "./ImperativeHandleMethod"; - -export function ImperativeHandle({ - json, - section -}: { - json: ImperativeHandleMetadata; - section: string; -}) { - return ( - - -
- - - - - - -
- {json.methods.map((method, index) => ( - - ))} -
-
- - ); -} diff --git a/src/components/handles/ImperativeHandleMethod.tsx b/src/components/handles/ImperativeHandleMethod.tsx deleted file mode 100644 index a552bb54..00000000 --- a/src/components/handles/ImperativeHandleMethod.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import type { ImperativeHandleMethodMetadata } from "../../types"; -import { Code } from "../code/Code"; -import { DocsSection } from "../DocsSection"; - -export function ImperativeHandleMethod({ - method -}: { - method: ImperativeHandleMethodMetadata; -}) { - return ( - <> -
{method.name}
-
- - -
- - ); -} diff --git a/src/components/props/ComponentProp.tsx b/src/components/props/ComponentProp.tsx deleted file mode 100644 index 1a0dc498..00000000 --- a/src/components/props/ComponentProp.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import type { ComponentPropMetadata } from "../../types"; -import { Code } from "../code/Code"; -import { DocsSection } from "../DocsSection"; - -export function ComponentProp({ prop }: { prop: ComponentPropMetadata }) { - return ( - <> -
- -
-
- -
- - ); -} diff --git a/src/components/props/ComponentProps.tsx b/src/components/props/ComponentProps.tsx deleted file mode 100644 index c26ba9f7..00000000 --- a/src/components/props/ComponentProps.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { ArrowTopRightOnSquareIcon } from "@heroicons/react/20/solid"; -import { useMemo } from "react"; -import { repository } from "../../../package.json"; -import type { ComponentMetadata } from "../../types"; -import { processPropsJSON } from "../../utils/processPropsJSON"; -import { Box } from "../Box"; -import { DocsSection } from "../DocsSection"; -import { ExternalLink } from "../ExternalLink"; -import { Header } from "../Header"; -import { ComponentPropsSection } from "./ComponentPropsSection"; - -export function ComponentProps({ - json, - section -}: { - json: ComponentMetadata; - section: string; -}) { - const { optionalProps, requiredProps } = useMemo( - () => processPropsJSON(json), - [json] - ); - - return ( - - -
- - - - - - - - - ); -} diff --git a/src/components/props/ComponentPropsSection.tsx b/src/components/props/ComponentPropsSection.tsx deleted file mode 100644 index 5fb62563..00000000 --- a/src/components/props/ComponentPropsSection.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import type { ComponentPropMetadata } from "../../types"; -import { Box } from "../Box"; -import { ComponentProp } from "./ComponentProp"; - -export function ComponentPropsSection({ - header, - props -}: { - header: string; - props: ComponentPropMetadata[]; -}) { - if (props.length === 0) { - return null; - } - - return ( - -
{header}
-
- {props.map((prop) => ( - - ))} -
-
- ); -} diff --git a/src/hooks/useNavStore.tsx b/src/hooks/useNavStore.tsx deleted file mode 100644 index 25948302..00000000 --- a/src/hooks/useNavStore.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import { create } from "zustand"; - -export const useNavStore = create<{ - visible: boolean; - - hide: () => void; - show: () => void; - toggle: () => void; -}>((set) => ({ - visible: false, - - hide: () => set({ visible: false }), - show: () => set({ visible: true }), - toggle: () => set((state) => ({ visible: !state.visible })) -})); diff --git a/src/nav/MobileNav.tsx b/src/nav/MobileNav.tsx deleted file mode 100644 index 4c90452c..00000000 --- a/src/nav/MobileNav.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import { useLayoutEffect } from "react"; -import { useLocation } from "react-router-dom"; -import { useNavStore } from "../hooks/useNavStore"; -import { cn } from "../utils/cn"; - -export function Nav() { - const { hide, visible } = useNavStore(); - - const { pathname } = useLocation(); - useLayoutEffect(() => { - hide(); - }, [hide, pathname]); - - return ( - - ); -} diff --git a/src/nav/Nav.tsx b/src/nav/Nav.tsx deleted file mode 100644 index e2c04a3c..00000000 --- a/src/nav/Nav.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import { NavLink } from "./NavLink"; -import { NavSection } from "./NavSection"; - -export function Nav() { - return ( -
-
- Getting started - How does it work? -
- - Fixed row heights - Variable row heights - Dynamic row heights - Scroll to row - ARIA roles - List props - Imperative handle - - - Tabular data - ARIA roles - - - Rendering a grid - Scroll to cells - ARIA roles - Grid props - Imperative handle - - - Right to left content - Horizontal lists - Images - Sticky rows - -
- Requirements - Support -
-
- ); -} diff --git a/src/nav/NavButton.tsx b/src/nav/NavButton.tsx deleted file mode 100644 index e796921b..00000000 --- a/src/nav/NavButton.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import type { PropsWithChildren } from "react"; -import { Box } from "../components/Box"; -import { cn } from "../utils/cn"; - -export function NavButton({ - children, - className, - disabled -}: PropsWithChildren<{ className?: string; disabled?: boolean }>) { - return ( - - {children} - - ); -} diff --git a/src/nav/NavLink.tsx b/src/nav/NavLink.tsx deleted file mode 100644 index 88af6b6f..00000000 --- a/src/nav/NavLink.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { type PropsWithChildren } from "react"; -import { Box } from "../components/Box"; -import { TransitionLink } from "../components/TransitionLink"; -import { type Path } from "../routes"; -import { cn } from "../utils/cn"; -import { NavButton } from "./NavButton"; - -export function NavLink({ - children, - className, - path -}: PropsWithChildren<{ - className?: string; - path: Path; -}>) { - return ( - - {({ isActive, isPending }) => ( - - - {children} - - - )} - - ); -} diff --git a/src/nav/NavRoute.tsx b/src/nav/NavRoute.tsx deleted file mode 100644 index a38ed273..00000000 --- a/src/nav/NavRoute.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import type { ReactNode } from "react"; -import { Route } from "react-router-dom"; - -export function NavRoute({ - route -}: { - route: { - component: ReactNode; - path: string; - }; -}) { - return ; -} diff --git a/src/nav/NavSection.tsx b/src/nav/NavSection.tsx deleted file mode 100644 index 16225362..00000000 --- a/src/nav/NavSection.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { - Disclosure, - DisclosureButton, - DisclosurePanel -} from "@headlessui/react"; -import { ChevronRightIcon } from "@heroicons/react/20/solid"; -import type { PropsWithChildren, ReactNode } from "react"; -import { NavButton } from "./NavButton"; - -export function NavSection({ - children, - label -}: PropsWithChildren<{ label: ReactNode }>) { - return ( - - - -
- {label} -
-
- - - - {children} - - ); -} diff --git a/src/routes/GettingStartedRoute.tsx b/src/routes/GettingStartedRoute.tsx index 82c40b19..c10d2785 100644 --- a/src/routes/GettingStartedRoute.tsx +++ b/src/routes/GettingStartedRoute.tsx @@ -1,7 +1,4 @@ -import { Box } from "../components/Box"; -import { Callout } from "../components/Callout"; -import { ExternalLink } from "../components/ExternalLink"; -import { Header } from "../components/Header"; +import { Box, Callout, ExternalLink, Header } from "react-lib-tools"; import { Link } from "../components/Link"; export default function GettingStartedRoute() { diff --git a/src/routes/HowDoesItWorkRoute.tsx b/src/routes/HowDoesItWorkRoute.tsx index fb607abe..795d23ad 100644 --- a/src/routes/HowDoesItWorkRoute.tsx +++ b/src/routes/HowDoesItWorkRoute.tsx @@ -1,13 +1,8 @@ import { ChevronRightIcon } from "@heroicons/react/20/solid"; import type { PropsWithChildren } from "react"; -import BasicRowMarkdown from "../../public/generated/code-snippets/BasicRow.json"; -import { Box } from "../components/Box"; -import { Callout } from "../components/Callout"; -import { Code } from "../components/code/Code"; -import { ExternalLink } from "../components/ExternalLink"; -import { Header } from "../components/Header"; +import { Box, Callout, Code, ExternalLink, Header, cn } from "react-lib-tools"; +import BasicRowMarkdown from "../../public/generated/examples/BasicRow.json"; import { Link } from "../components/Link"; -import { cn } from "../utils/cn"; import { getIntentClassNames } from "../utils/getIntentClassNames"; export default function HowDoesItWorkRoute() { diff --git a/src/routes/PageNotFound.tsx b/src/routes/PageNotFound.tsx index 3dc8a9fe..30f5bdad 100644 --- a/src/routes/PageNotFound.tsx +++ b/src/routes/PageNotFound.tsx @@ -1,7 +1,4 @@ -import { Box } from "../components/Box"; -import { Callout } from "../components/Callout"; -import { ExternalLink } from "../components/ExternalLink"; -import { Header } from "../components/Header"; +import { Box, Callout, ExternalLink, Header } from "react-lib-tools"; export default function PageNotFound() { return ( diff --git a/src/routes/PlatformRequirementsRoute.tsx b/src/routes/PlatformRequirementsRoute.tsx index d15006a4..ed393652 100644 --- a/src/routes/PlatformRequirementsRoute.tsx +++ b/src/routes/PlatformRequirementsRoute.tsx @@ -1,7 +1,4 @@ -import { Box } from "../components/Box"; -import { Callout } from "../components/Callout"; -import { ExternalLink } from "../components/ExternalLink"; -import { Header } from "../components/Header"; +import { Box, Callout, ExternalLink, Header } from "react-lib-tools"; export default function PlatformRequirementsRoute() { return ( diff --git a/src/routes/SupportRoute.tsx b/src/routes/SupportRoute.tsx index 0260a732..fb63acf1 100644 --- a/src/routes/SupportRoute.tsx +++ b/src/routes/SupportRoute.tsx @@ -1,6 +1,4 @@ -import { Box } from "../components/Box"; -import { ExternalLink } from "../components/ExternalLink"; -import { Header } from "../components/Header"; +import { Box, ExternalLink, Header } from "react-lib-tools"; export default function SupportRoute() { return ( diff --git a/src/routes/VersionsRoute.tsx b/src/routes/VersionsRoute.tsx index 21dfd44b..bff95b0d 100644 --- a/src/routes/VersionsRoute.tsx +++ b/src/routes/VersionsRoute.tsx @@ -1,7 +1,5 @@ +import { Box, ExternalLink, Header } from "react-lib-tools"; import { Fragment } from "react/jsx-runtime"; -import { Box } from "../components/Box"; -import { ExternalLink } from "../components/ExternalLink"; -import { Header } from "../components/Header"; import GlobeIcon from "../../public/svgs/globe.svg?react"; import TagsIcon from "../../public/svgs/tags.svg?react"; diff --git a/src/routes/grid/AriaRolesRoute.tsx b/src/routes/grid/AriaRolesRoute.tsx index ac247e20..068db058 100644 --- a/src/routes/grid/AriaRolesRoute.tsx +++ b/src/routes/grid/AriaRolesRoute.tsx @@ -1,10 +1,7 @@ -import CellComponentAriaRolesMarkdown from "../../../public/generated/code-snippets/CellComponentAriaRoles.json"; -import GridAriaRolesMarkdown from "../../../public/generated/code-snippets/GridAriaRoles.json"; -import { Box } from "../../components/Box"; -import { Code } from "../../components/code/Code"; +import { Box, Code, ExternalLink, Header } from "react-lib-tools"; +import CellComponentAriaRolesMarkdown from "../../../public/generated/examples/CellComponentAriaRoles.json"; +import GridAriaRolesMarkdown from "../../../public/generated/examples/GridAriaRoles.json"; import { ContinueLink } from "../../components/ContinueLink"; -import { ExternalLink } from "../../components/ExternalLink"; -import { Header } from "../../components/Header"; export default function AriaRolesRoute() { return ( diff --git a/src/routes/grid/HorizontalListsRoute.tsx b/src/routes/grid/HorizontalListsRoute.tsx index 887ef9a0..e99c3b98 100644 --- a/src/routes/grid/HorizontalListsRoute.tsx +++ b/src/routes/grid/HorizontalListsRoute.tsx @@ -1,10 +1,6 @@ -import HorizontalListMarkdown from "../../../public/generated/code-snippets/HorizontalList.json"; -import HorizontalListCellRendererMarkdown from "../../../public/generated/code-snippets/HorizontalListCellRenderer.json"; -import { Block } from "../../components/Block"; -import { Box } from "../../components/Box"; -import { Code } from "../../components/code/Code"; -import { Header } from "../../components/Header"; -import { LoadingSpinner } from "../../components/LoadingSpinner"; +import { Block, Box, Code, Header, LoadingSpinner } from "react-lib-tools"; +import HorizontalListMarkdown from "../../../public/generated/examples/HorizontalList.json"; +import HorizontalListCellRendererMarkdown from "../../../public/generated/examples/HorizontalListCellRenderer.json"; import { HorizontalList } from "./examples/HorizontalList.example"; import { useEmails } from "./hooks/useEmails"; diff --git a/src/routes/grid/ImperativeHandleRoute.tsx b/src/routes/grid/ImperativeHandleRoute.tsx index 75091d1a..dc18e316 100644 --- a/src/routes/grid/ImperativeHandleRoute.tsx +++ b/src/routes/grid/ImperativeHandleRoute.tsx @@ -1,10 +1,7 @@ -import { html as useGridCallbackRefHTML } from "../../../public/generated/code-snippets/useGridCallbackRef.json"; -import { html as useGridRefHTML } from "../../../public/generated/code-snippets/useGridRef.json"; -import json from "../../../public/generated/js-docs/GridImperativeAPI.json"; -import { Box } from "../../components/Box"; -import { Code } from "../../components/code/Code"; -import { ExternalLink } from "../../components/ExternalLink"; -import { ImperativeHandle } from "../../components/handles/ImperativeHandle"; +import { Box, Code, ExternalLink, ImperativeHandle } from "react-lib-tools"; +import { html as useGridCallbackRefHTML } from "../../../public/generated/examples/useGridCallbackRef.json"; +import { html as useGridRefHTML } from "../../../public/generated/examples/useGridRef.json"; +import json from "../../../public/generated/docs/GridImperativeAPI.json"; import type { ImperativeHandleMetadata } from "../../types"; export default function GridImperativeHandleRoute() { diff --git a/src/routes/grid/PropsRoute.tsx b/src/routes/grid/PropsRoute.tsx index f011b8f4..b24b72e0 100644 --- a/src/routes/grid/PropsRoute.tsx +++ b/src/routes/grid/PropsRoute.tsx @@ -1,6 +1,5 @@ -import json from "../../../public/generated/js-docs/Grid.json"; -import { Box } from "../../components/Box"; -import { ComponentProps } from "../../components/props/ComponentProps"; +import { Box, ComponentProps } from "react-lib-tools"; +import json from "../../../public/generated/docs/Grid.json"; import type { ComponentMetadata } from "../../types"; export default function GridPropsRoute() { diff --git a/src/routes/grid/RTLGridsRoute.tsx b/src/routes/grid/RTLGridsRoute.tsx index 8d39f4cb..8ca49714 100644 --- a/src/routes/grid/RTLGridsRoute.tsx +++ b/src/routes/grid/RTLGridsRoute.tsx @@ -1,10 +1,12 @@ -import RtlGridMarkdown from "../../../public/generated/code-snippets/RtlGrid.json"; -import { Block } from "../../components/Block"; -import { Box } from "../../components/Box"; -import { Code } from "../../components/code/Code"; -import { ExternalLink } from "../../components/ExternalLink"; -import { Header } from "../../components/Header"; -import { LoadingSpinner } from "../../components/LoadingSpinner"; +import { + Block, + Box, + Code, + ExternalLink, + Header, + LoadingSpinner +} from "react-lib-tools"; +import RtlGridMarkdown from "../../../public/generated/examples/RtlGrid.json"; import { RtlExample } from "./examples/RtlGrid.example"; import { useContacts } from "./hooks/useContacts"; diff --git a/src/routes/grid/RenderingGridRoute.tsx b/src/routes/grid/RenderingGridRoute.tsx index 00763aa1..f16eaec9 100644 --- a/src/routes/grid/RenderingGridRoute.tsx +++ b/src/routes/grid/RenderingGridRoute.tsx @@ -1,14 +1,16 @@ -import CellComponentMarkdown from "../../../public/generated/code-snippets/CellComponent.json"; -import columnWidthMarkdown from "../../../public/generated/code-snippets/columnWidth.json"; -import GridMarkdown from "../../../public/generated/code-snippets/Grid.json"; -import { Block } from "../../components/Block"; -import { Box } from "../../components/Box"; -import { Callout } from "../../components/Callout"; -import { Code } from "../../components/code/Code"; +import { + Block, + Box, + Callout, + Code, + ExternalLink, + Header, + LoadingSpinner +} from "react-lib-tools"; +import CellComponentMarkdown from "../../../public/generated/examples/CellComponent.json"; +import columnWidthMarkdown from "../../../public/generated/examples/columnWidth.json"; +import GridMarkdown from "../../../public/generated/examples/Grid.json"; import { ContinueLink } from "../../components/ContinueLink"; -import { ExternalLink } from "../../components/ExternalLink"; -import { Header } from "../../components/Header"; -import { LoadingSpinner } from "../../components/LoadingSpinner"; import { Example } from "./examples/Grid.example"; import { useContacts } from "./hooks/useContacts"; diff --git a/src/routes/grid/ScrollToCellRoute.tsx b/src/routes/grid/ScrollToCellRoute.tsx index 874f70f8..0e2fccfe 100644 --- a/src/routes/grid/ScrollToCellRoute.tsx +++ b/src/routes/grid/ScrollToCellRoute.tsx @@ -1,23 +1,26 @@ import { useMemo, useState } from "react"; +import { + Block, + Box, + Button, + Callout, + Code, + Header, + LoadingSpinner, + Select, + type Option +} from "react-lib-tools"; import { Grid, useGridRef, type Align } from "react-window"; -import gridRefClickEventHandlerMarkdown from "../../../public/generated/code-snippets/gridRefClickEventHandler.json"; -import useGridCallbackRefMarkdown from "../../../public/generated/code-snippets/useGridCallbackRef.json"; -import useGridRefMarkdown from "../../../public/generated/code-snippets/useGridRef.json"; -import useGridRefImportMarkdown from "../../../public/generated/code-snippets/useGridRefImport.json"; -import { Block } from "../../components/Block"; -import { Box } from "../../components/Box"; -import { Button } from "../../components/Button"; -import { Callout } from "../../components/Callout"; -import { Code } from "../../components/code/Code"; -import { Header } from "../../components/Header"; -import { LoadingSpinner } from "../../components/LoadingSpinner"; -import { Select, type Option } from "../../components/Select"; +import gridRefClickEventHandlerMarkdown from "../../../public/generated/examples/gridRefClickEventHandler.json"; +import useGridCallbackRefMarkdown from "../../../public/generated/examples/useGridCallbackRef.json"; +import useGridRefMarkdown from "../../../public/generated/examples/useGridRef.json"; +import useGridRefImportMarkdown from "../../../public/generated/examples/useGridRefImport.json"; +import { ContinueLink } from "../../components/ContinueLink"; import { CellComponent } from "./examples/CellComponent.example"; import { columnWidth } from "./examples/columnWidth.example"; import type { Contact } from "./examples/Grid.example"; import { COLUMN_KEYS } from "./examples/shared"; import { useContacts } from "./hooks/useContacts"; -import { ContinueLink } from "../../components/ContinueLink"; const EMPTY_OPTION: Option = { label: "", diff --git a/src/routes/grid/examples/HorizontalListCellRenderer.example.tsx b/src/routes/grid/examples/HorizontalListCellRenderer.example.tsx index 89c08aef..d2450ac3 100644 --- a/src/routes/grid/examples/HorizontalListCellRenderer.example.tsx +++ b/src/routes/grid/examples/HorizontalListCellRenderer.example.tsx @@ -1,4 +1,4 @@ -import { cn } from "../../../utils/cn"; +import { cn } from "react-lib-tools"; // diff --git a/src/routes/list/AriaRolesRoute.tsx b/src/routes/list/AriaRolesRoute.tsx index 7520f097..aae29657 100644 --- a/src/routes/list/AriaRolesRoute.tsx +++ b/src/routes/list/AriaRolesRoute.tsx @@ -1,10 +1,7 @@ -import ListAriaRolesMarkdown from "../../../public/generated/code-snippets/ListAriaRoles.json"; -import RowComponentAriaRolesMarkdown from "../../../public/generated/code-snippets/RowComponentAriaRoles.json"; -import { Box } from "../../components/Box"; -import { Code } from "../../components/code/Code"; +import { Box, Code, ExternalLink, Header } from "react-lib-tools"; +import ListAriaRolesMarkdown from "../../../public/generated/examples/ListAriaRoles.json"; +import RowComponentAriaRolesMarkdown from "../../../public/generated/examples/RowComponentAriaRoles.json"; import { ContinueLink } from "../../components/ContinueLink"; -import { ExternalLink } from "../../components/ExternalLink"; -import { Header } from "../../components/Header"; export default function AriaRolesRoute() { return ( diff --git a/src/routes/list/DynamicRowHeightsRoute.tsx b/src/routes/list/DynamicRowHeightsRoute.tsx index be49157f..99402b75 100644 --- a/src/routes/list/DynamicRowHeightsRoute.tsx +++ b/src/routes/list/DynamicRowHeightsRoute.tsx @@ -1,14 +1,16 @@ -import { Block } from "../../components/Block"; -import { Box } from "../../components/Box"; -import { Callout } from "../../components/Callout"; -import { Code } from "../../components/code/Code"; +import { + Block, + Box, + Callout, + Code, + Header, + LoadingSpinner +} from "react-lib-tools"; +import ListDynamicRowHeightsMarkdown from "../../../public/generated/examples/ListDynamicRowHeights.json"; +import ListRowDynamicRowHeightsMarkdown from "../../../public/generated/examples/ListRowDynamicRowHeights.json"; import { ContinueLink } from "../../components/ContinueLink"; -import { Header } from "../../components/Header"; -import { LoadingSpinner } from "../../components/LoadingSpinner"; import { Example } from "./examples/ListDynamicRowHeights.example"; import { useLorem } from "./hooks/useLorem"; -import ListDynamicRowHeightsMarkdown from "../../../public/generated/code-snippets/ListDynamicRowHeights.json"; -import ListRowDynamicRowHeightsMarkdown from "../../../public/generated/code-snippets/ListRowDynamicRowHeights.json"; export default function DynamicRowHeightsRoute() { const lorem = useLorem(); diff --git a/src/routes/list/FixedRowHeightsRoute.tsx b/src/routes/list/FixedRowHeightsRoute.tsx index 559b8650..e74c92a0 100644 --- a/src/routes/list/FixedRowHeightsRoute.tsx +++ b/src/routes/list/FixedRowHeightsRoute.tsx @@ -1,13 +1,15 @@ +import { + Block, + Box, + Callout, + Code, + ExternalLink, + Header +} from "react-lib-tools"; import json from "../../../public/data/names.json"; -import FixedHeightListMarkdown from "../../../public/generated/code-snippets/FixedHeightList.json"; -import FixedHeightRowComponentMarkdown from "../../../public/generated/code-snippets/FixedHeightRowComponent.json"; -import { Block } from "../../components/Block"; -import { Box } from "../../components/Box"; -import { Callout } from "../../components/Callout"; -import { Code } from "../../components/code/Code"; +import FixedHeightListMarkdown from "../../../public/generated/examples/FixedHeightList.json"; +import FixedHeightRowComponentMarkdown from "../../../public/generated/examples/FixedHeightRowComponent.json"; import { ContinueLink } from "../../components/ContinueLink"; -import { ExternalLink } from "../../components/ExternalLink"; -import { Header } from "../../components/Header"; import { Example } from "./examples/FixedHeightList.example"; export default function FixedRowHeightsRoute() { diff --git a/src/routes/list/ImagesRoute.tsx b/src/routes/list/ImagesRoute.tsx index 0b5d4f6e..5d374820 100644 --- a/src/routes/list/ImagesRoute.tsx +++ b/src/routes/list/ImagesRoute.tsx @@ -1,11 +1,8 @@ -import { Block } from "../../components/Block"; -import { Box } from "../../components/Box"; -import { Code } from "../../components/code/Code"; -import { Header } from "../../components/Header"; -import { ExampleWithImages } from "./examples/Images.example"; -import ImageRowMarkdown from "../../../public/generated/code-snippets/ImageRow.json"; -import ImagesMarkdown from "../../../public/generated/code-snippets/Images.json"; +import { Block, Box, Code, Header } from "react-lib-tools"; +import ImageRowMarkdown from "../../../public/generated/examples/ImageRow.json"; +import ImagesMarkdown from "../../../public/generated/examples/Images.json"; import { Link } from "../../components/Link"; +import { ExampleWithImages } from "./examples/Images.example"; export default function ImagesRoute() { return ( diff --git a/src/routes/list/ImperativeApiRoute.tsx b/src/routes/list/ImperativeApiRoute.tsx index 413feadf..9f7a82a3 100644 --- a/src/routes/list/ImperativeApiRoute.tsx +++ b/src/routes/list/ImperativeApiRoute.tsx @@ -1,11 +1,8 @@ -import json from "../../../public/generated/js-docs/ListImperativeAPI.json"; -import { Box } from "../../components/Box"; -import { Code } from "../../components/code/Code"; -import { ImperativeHandle } from "../../components/handles/ImperativeHandle"; +import { Box, Code, ExternalLink, ImperativeHandle } from "react-lib-tools"; +import { html as useListCallbackRefHTML } from "../../../public/generated/examples/useListCallbackRef.json"; +import { html as useListRefHTML } from "../../../public/generated/examples/useListRef.json"; +import json from "../../../public/generated/docs/ListImperativeAPI.json"; import type { ImperativeHandleMetadata } from "../../types"; -import { html as useListRefHTML } from "../../../public/generated/code-snippets/useListRef.json"; -import { html as useListCallbackRefHTML } from "../../../public/generated/code-snippets/useListCallbackRef.json"; -import { ExternalLink } from "../../components/ExternalLink"; export default function ListImperativeApiRoute() { return ( diff --git a/src/routes/list/PropsRoute.tsx b/src/routes/list/PropsRoute.tsx index bac82a3e..a3e0c021 100644 --- a/src/routes/list/PropsRoute.tsx +++ b/src/routes/list/PropsRoute.tsx @@ -1,6 +1,5 @@ -import json from "../../../public/generated/js-docs/List.json"; -import { Box } from "../../components/Box"; -import { ComponentProps } from "../../components/props/ComponentProps"; +import { Box, ComponentProps } from "react-lib-tools"; +import json from "../../../public/generated/docs/List.json"; import type { ComponentMetadata } from "../../types"; export default function ListPropsRoute() { diff --git a/src/routes/list/ScrollToRowRoute.tsx b/src/routes/list/ScrollToRowRoute.tsx index 9b157ce7..16f1450c 100644 --- a/src/routes/list/ScrollToRowRoute.tsx +++ b/src/routes/list/ScrollToRowRoute.tsx @@ -1,21 +1,24 @@ import { useMemo, useState } from "react"; +import { + Block, + Box, + Button, + Callout, + Code, + Header, + LoadingSpinner, + Select, + type Option +} from "react-lib-tools"; import { List, useListRef, type Align } from "react-window"; -import listRefClickEventHandlerMarkdown from "../../../public/generated/code-snippets/listRefClickEventHandler.json"; -import useListCallbackRefMarkdown from "../../../public/generated/code-snippets/useListCallbackRef.json"; -import useListRefMarkdown from "../../../public/generated/code-snippets/useListRef.json"; -import useListRefImportMarkdown from "../../../public/generated/code-snippets/useListRefImport.json"; -import { Block } from "../../components/Block"; -import { Box } from "../../components/Box"; -import { Button } from "../../components/Button"; -import { Callout } from "../../components/Callout"; -import { Code } from "../../components/code/Code"; -import { Header } from "../../components/Header"; -import { LoadingSpinner } from "../../components/LoadingSpinner"; -import { Select, type Option } from "../../components/Select"; +import listRefClickEventHandlerMarkdown from "../../../public/generated/examples/listRefClickEventHandler.json"; +import useListCallbackRefMarkdown from "../../../public/generated/examples/useListCallbackRef.json"; +import useListRefMarkdown from "../../../public/generated/examples/useListRef.json"; +import useListRefImportMarkdown from "../../../public/generated/examples/useListRefImport.json"; +import { ContinueLink } from "../../components/ContinueLink"; import { RowComponent } from "./examples/ListVariableRowHeights.example"; import { rowHeight } from "./examples/rowHeight.example"; import { useCitiesByState } from "./hooks/useCitiesByState"; -import { ContinueLink } from "../../components/ContinueLink"; const EMPTY_OPTION: Option = { label: "", diff --git a/src/routes/list/StickyRowsRoute.tsx b/src/routes/list/StickyRowsRoute.tsx index 6c1cc3db..cb63570d 100644 --- a/src/routes/list/StickyRowsRoute.tsx +++ b/src/routes/list/StickyRowsRoute.tsx @@ -1,10 +1,12 @@ -import ListWithStickyRowsMarkdown from "../../../public/generated/code-snippets/ListWithStickyRows.json"; -import { Block } from "../../components/Block"; -import { Box } from "../../components/Box"; -import { Callout } from "../../components/Callout"; -import { Code } from "../../components/code/Code"; -import { ExternalLink } from "../../components/ExternalLink"; -import { Header } from "../../components/Header"; +import { + Block, + Box, + Callout, + Code, + ExternalLink, + Header +} from "react-lib-tools"; +import ListWithStickyRowsMarkdown from "../../../public/generated/examples/ListWithStickyRows.json"; import { Example } from "./examples/ListWithStickyRows.example"; export default function StickyRowsRoute() { diff --git a/src/routes/list/VariableRowHeightsRoute.tsx b/src/routes/list/VariableRowHeightsRoute.tsx index 88347971..dede9db6 100644 --- a/src/routes/list/VariableRowHeightsRoute.tsx +++ b/src/routes/list/VariableRowHeightsRoute.tsx @@ -1,10 +1,6 @@ -import ListVariableRowHeightsMarkdown from "../../../public/generated/code-snippets/ListVariableRowHeights.json"; -import { Block } from "../../components/Block"; -import { Box } from "../../components/Box"; -import { Code } from "../../components/code/Code"; +import { Block, Box, Code, Header, LoadingSpinner } from "react-lib-tools"; +import ListVariableRowHeightsMarkdown from "../../../public/generated/examples/ListVariableRowHeights.json"; import { ContinueLink } from "../../components/ContinueLink"; -import { Header } from "../../components/Header"; -import { LoadingSpinner } from "../../components/LoadingSpinner"; import { Example } from "./examples/ListVariableRowHeights.example"; import { useCitiesByState } from "./hooks/useCitiesByState"; diff --git a/src/routes/list/examples/ImageRow.example.tsx b/src/routes/list/examples/ImageRow.example.tsx index f3690ea8..757aa992 100644 --- a/src/routes/list/examples/ImageRow.example.tsx +++ b/src/routes/list/examples/ImageRow.example.tsx @@ -1,4 +1,4 @@ -import { Box } from "../../../components/Box"; +import { Box } from "react-lib-tools"; // diff --git a/src/routes/list/examples/ListRowDynamicRowHeights.example.tsx b/src/routes/list/examples/ListRowDynamicRowHeights.example.tsx index 2e2b554a..b50e42a6 100644 --- a/src/routes/list/examples/ListRowDynamicRowHeights.example.tsx +++ b/src/routes/list/examples/ListRowDynamicRowHeights.example.tsx @@ -1,5 +1,5 @@ import { MinusIcon, PlusIcon } from "@heroicons/react/20/solid"; -import { cn } from "../../../utils/cn"; +import { cn } from "react-lib-tools"; import type { ListState } from "./ListDynamicRowHeights.example"; // diff --git a/src/routes/tables/AriaRolesRoute.tsx b/src/routes/tables/AriaRolesRoute.tsx index 7fdb0b40..9ab34fe4 100644 --- a/src/routes/tables/AriaRolesRoute.tsx +++ b/src/routes/tables/AriaRolesRoute.tsx @@ -1,9 +1,6 @@ -import TableAriaAttributesMarkdown from "../../../public/generated/code-snippets/TableAriaAttributes.json"; -import TableAriaOverridePropsMarkdown from "../../../public/generated/code-snippets/TableAriaOverrideProps.json"; -import { Box } from "../../components/Box"; -import { Code } from "../../components/code/Code"; -import { ExternalLink } from "../../components/ExternalLink"; -import { Header } from "../../components/Header"; +import { Box, Code, ExternalLink, Header } from "react-lib-tools"; +import TableAriaAttributesMarkdown from "../../../public/generated/examples/TableAriaAttributes.json"; +import TableAriaOverridePropsMarkdown from "../../../public/generated/examples/TableAriaOverrideProps.json"; export default function AriaRolesRoute() { return ( diff --git a/src/routes/tables/TabularDataRoute.tsx b/src/routes/tables/TabularDataRoute.tsx index ece945b0..3962aa43 100644 --- a/src/routes/tables/TabularDataRoute.tsx +++ b/src/routes/tables/TabularDataRoute.tsx @@ -1,12 +1,14 @@ -import FlexboxLayoutMarkdown from "../../../public/generated/code-snippets/FlexboxLayout.json"; -import { Block } from "../../components/Block"; -import { Box } from "../../components/Box"; -import { Callout } from "../../components/Callout"; -import { Code } from "../../components/code/Code"; +import { + Block, + Box, + Callout, + Code, + Header, + LoadingSpinner +} from "react-lib-tools"; +import FlexboxLayoutMarkdown from "../../../public/generated/examples/FlexboxLayout.json"; import { ContinueLink } from "../../components/ContinueLink"; -import { Header } from "../../components/Header"; import { Link } from "../../components/Link"; -import { LoadingSpinner } from "../../components/LoadingSpinner"; import { Example } from "./examples/FlexboxLayout.example"; import { useAddresses } from "./hooks/useAddresses"; diff --git a/src/utils/cn.ts b/src/utils/cn.ts deleted file mode 100644 index 365058ce..00000000 --- a/src/utils/cn.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { type ClassValue, clsx } from "clsx"; -import { twMerge } from "tailwind-merge"; - -export function cn(...inputs: ClassValue[]) { - return twMerge(clsx(inputs)); -}