diff --git a/README.md b/README.md
index a55e21e1..ddb5a59f 100644
--- a/README.md
+++ b/README.md
@@ -238,8 +238,8 @@ Grid will automatically re-render cells when values in this object change.
dir
- Corresponds to the HTML dir attribute:
-https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/dir
+ Indicates the directionality of grid cells.
+ℹ️ See HTML dir global attribute for more information.
@@ -268,8 +268,7 @@ This value is important for server rendering.
gridRef
- Ref used to interact with this component's imperative API.
-This API has imperative methods for scrolling and a getter for the outermost DOM element.
+ Imperative Grid API.
ℹ️ The useGridRef and useGridCallbackRef hooks are exported for convenience use in TypeScript projects.
diff --git a/lib/components/grid/Grid.tsx b/lib/components/grid/Grid.tsx
index 6b59842f..f37a7cdd 100644
--- a/lib/components/grid/Grid.tsx
+++ b/lib/components/grid/Grid.tsx
@@ -15,6 +15,9 @@ import type { Align, TagNames } from "../../types";
import { arePropsEqual } from "../../utils/arePropsEqual";
import type { GridProps } from "./types";
+/**
+ * Renders data with many rows and columns.
+ */
export function Grid<
CellProps extends object,
TagName extends TagNames = "div"
diff --git a/lib/components/grid/types.ts b/lib/components/grid/types.ts
index beb831c7..a794318a 100644
--- a/lib/components/grid/types.ts
+++ b/lib/components/grid/types.ts
@@ -85,15 +85,14 @@ export type GridProps<
defaultWidth?: number;
/**
- * Corresponds to the HTML dir attribute:
- * https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/dir
+ * Indicates the directionality of grid cells.
+ *
+ * ℹ️ See HTML `dir` [global attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/dir) for more information.
*/
dir?: "ltr" | "rtl";
/**
- * Ref used to interact with this component's imperative API.
- *
- * This API has imperative methods for scrolling and a getter for the outermost DOM element.
+ * Imperative Grid API.
*
* ℹ️ The `useGridRef` and `useGridCallbackRef` hooks are exported for convenience use in TypeScript projects.
*/
@@ -244,7 +243,7 @@ export type CachedBounds = Map<
*
* ℹ️ The `useGridRef` and `useGridCallbackRef` hooks are exported for convenience use in TypeScript projects.
*/
-export type GridImperativeAPI = {
+export interface GridImperativeAPI {
/**
* Outermost HTML element for the grid if mounted and null (if not mounted.
*/
@@ -261,7 +260,7 @@ export type GridImperativeAPI = {
*
* @throws RangeError if an invalid row or column index is provided
*/
- scrollToCell({
+ scrollToCell: ({
behavior,
columnAlign,
columnIndex,
@@ -273,7 +272,7 @@ export type GridImperativeAPI = {
columnIndex: number;
rowAlign?: "auto" | "center" | "end" | "smart" | "start";
rowIndex: number;
- }): void;
+ }) => void;
/**
* Scrolls the grid so that the specified column is visible.
@@ -284,7 +283,7 @@ export type GridImperativeAPI = {
*
* @throws RangeError if an invalid column index is provided
*/
- scrollToColumn({
+ scrollToColumn: ({
align,
behavior,
index
@@ -292,7 +291,7 @@ export type GridImperativeAPI = {
align?: "auto" | "center" | "end" | "smart" | "start";
behavior?: "auto" | "instant" | "smooth";
index: number;
- }): void;
+ }) => void;
/**
* Scrolls the grid so that the specified row is visible.
@@ -303,7 +302,7 @@ export type GridImperativeAPI = {
*
* @throws RangeError if an invalid row index is provided
*/
- scrollToRow({
+ scrollToRow: ({
align,
behavior,
index
@@ -311,5 +310,5 @@ export type GridImperativeAPI = {
align?: "auto" | "center" | "end" | "smart" | "start";
behavior?: "auto" | "instant" | "smooth";
index: number;
- }): void;
-};
+ }) => void;
+}
diff --git a/lib/components/list/List.tsx b/lib/components/list/List.tsx
index 4f47f6d6..d138772e 100644
--- a/lib/components/list/List.tsx
+++ b/lib/components/list/List.tsx
@@ -18,6 +18,9 @@ import type { ListProps } from "./types";
export const DATA_ATTRIBUTE_LIST_INDEX = "data-react-window-index";
+/**
+ * Renders data with many rows.
+ */
export function List<
RowProps extends object,
TagName extends TagNames = "div"
diff --git a/lib/components/list/types.ts b/lib/components/list/types.ts
index 865c7571..1491cf5b 100644
--- a/lib/components/list/types.ts
+++ b/lib/components/list/types.ts
@@ -174,13 +174,11 @@ export type CachedBounds = Map<
>;
/**
- * Ref used to interact with this component's imperative API.
- *
- * This API has imperative methods for scrolling and a getter for the outermost DOM element.
+ * Imperative List API.
*
* ℹ️ The `useListRef` and `useListCallbackRef` hooks are exported for convenience use in TypeScript projects.
*/
-export type ListImperativeAPI = {
+export interface ListImperativeAPI {
/**
* Outermost HTML element for the list if mounted and null (if not mounted.
*/
@@ -195,7 +193,7 @@ export type ListImperativeAPI = {
*
* @throws RangeError if an invalid row index is provided
*/
- scrollToRow({
+ scrollToRow: ({
align,
behavior,
index
@@ -203,5 +201,5 @@ export type ListImperativeAPI = {
align?: "auto" | "center" | "end" | "smart" | "start";
behavior?: "auto" | "instant" | "smooth";
index: number;
- }): void;
-};
+ }) => void;
+}
diff --git a/package.json b/package.json
index df853e14..4a243816 100644
--- a/package.json
+++ b/package.json
@@ -61,6 +61,8 @@
"@testing-library/jest-dom": "^6.6.4",
"@testing-library/react": "^16.3.0",
"@testing-library/user-event": "^14.6.1",
+ "@ts-ast-parser/core": "^0.8.0",
+ "@types/markdown-it": "^14.1.2",
"@types/node": "^24.2.0",
"@types/react": "^19.1.8",
"@types/react-dom": "^19.1.6",
@@ -74,6 +76,7 @@
"husky": "^9.1.7",
"jsdom": "^26.1.0",
"lint-staged": "^16.1.4",
+ "markdown-it": "^14.1.0",
"marked": "^16.4.1",
"postcss": "^8.5.6",
"prettier": "3.6.2",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index a0a80cc1..9f9fd9ce 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -58,6 +58,12 @@ importers:
"@testing-library/user-event":
specifier: ^14.6.1
version: 14.6.1(@testing-library/dom@10.4.0)
+ "@ts-ast-parser/core":
+ specifier: ^0.8.0
+ version: 0.8.0(typescript@5.8.3)
+ "@types/markdown-it":
+ specifier: ^14.1.2
+ version: 14.1.2
"@types/node":
specifier: ^24.2.0
version: 24.2.0
@@ -97,6 +103,9 @@ importers:
lint-staged:
specifier: ^16.1.4
version: 16.1.4
+ markdown-it:
+ specifier: ^14.1.0
+ version: 14.1.0
marked:
specifier: ^16.4.1
version: 16.4.1
@@ -162,7 +171,7 @@ importers:
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))
vitest:
specifier: ^3.2.4
- version: 3.2.4(@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)(yaml@2.8.0)
zustand:
specifier: ^5.0.7
version: 5.0.7(@types/react@19.1.8)(react@19.1.0)(use-sync-external-store@1.5.0(react@19.1.0))
@@ -1274,6 +1283,13 @@ packages:
integrity: sha512-+AkJDbu1GFMPIU8Sb7TLVXDv/Q7Mkvx+wAjEl8XiXVVq+p1FmWW6M3LYpJMmoHNckSofeMecgWg5lfMwNAAsEQ==
}
+ "@sindresorhus/merge-streams@2.3.0":
+ resolution:
+ {
+ integrity: sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==
+ }
+ engines: { node: ">=18" }
+
"@svgr/babel-plugin-add-jsx-attribute@8.0.0":
resolution:
{
@@ -1695,6 +1711,22 @@ packages:
peerDependencies:
"@testing-library/dom": ">=7.21.4"
+ "@ts-ast-parser/comment@0.2.0":
+ resolution:
+ {
+ integrity: sha512-W/nxZGgoK0HS90Zuy5tg4I2TF6izsW3U8jIHk+M9dEvpBjvVMLOZCmZbaewsOsu8xA11vAiLAD4IMBjLMuUwcQ==
+ }
+ engines: { node: 18.x || 20.x || 21.x }
+
+ "@ts-ast-parser/core@0.8.0":
+ resolution:
+ {
+ integrity: sha512-UN78bQwfayfefhJ5f3Z7GnSTh7/oKC1G/Fp5BIR6NxP8EebEC8DRmtSDIBwO6nnLVJjI69W0R57Fa3/EzgtcvA==
+ }
+ engines: { node: 18.x || 20.x || 21.x }
+ peerDependencies:
+ typescript: ^4.6.x || 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x
+
"@tsconfig/node10@1.0.11":
resolution:
{
@@ -1737,6 +1769,12 @@ packages:
integrity: sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==
}
+ "@types/debug@4.1.12":
+ resolution:
+ {
+ integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==
+ }
+
"@types/deep-eql@4.0.2":
resolution:
{
@@ -1755,6 +1793,30 @@ packages:
integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==
}
+ "@types/linkify-it@5.0.0":
+ resolution:
+ {
+ integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==
+ }
+
+ "@types/markdown-it@14.1.2":
+ resolution:
+ {
+ integrity: sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==
+ }
+
+ "@types/mdurl@2.0.0":
+ resolution:
+ {
+ integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==
+ }
+
+ "@types/ms@2.1.0":
+ resolution:
+ {
+ integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==
+ }
+
"@types/node@18.19.121":
resolution:
{
@@ -2831,6 +2893,13 @@ packages:
}
engines: { node: ">=18" }
+ globby@14.1.0:
+ resolution:
+ {
+ integrity: sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==
+ }
+ engines: { node: ">=18" }
+
graceful-fs@4.2.11:
resolution:
{
@@ -3269,6 +3338,12 @@ packages:
integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==
}
+ linkify-it@5.0.0:
+ resolution:
+ {
+ integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==
+ }
+
lint-staged@16.1.4:
resolution:
{
@@ -3367,6 +3442,13 @@ packages:
integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==
}
+ markdown-it@14.1.0:
+ resolution:
+ {
+ integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==
+ }
+ hasBin: true
+
marked@16.4.1:
resolution:
{
@@ -3375,6 +3457,12 @@ packages:
engines: { node: ">= 20" }
hasBin: true
+ mdurl@2.0.0:
+ resolution:
+ {
+ integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==
+ }
+
merge-stream@2.0.0:
resolution:
{
@@ -3615,6 +3703,13 @@ packages:
}
engines: { node: ">=8" }
+ path-type@6.0.0:
+ resolution:
+ {
+ integrity: sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==
+ }
+ engines: { node: ">=18" }
+
pathe@2.0.3:
resolution:
{
@@ -3761,6 +3856,13 @@ packages:
}
engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 }
+ punycode.js@2.3.1:
+ resolution:
+ {
+ integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==
+ }
+ engines: { node: ">=6" }
+
punycode@2.3.1:
resolution:
{
@@ -4041,6 +4143,13 @@ packages:
}
engines: { node: ">=14" }
+ slash@5.1.0:
+ resolution:
+ {
+ integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==
+ }
+ engines: { node: ">=14.16" }
+
slice-ansi@5.0.0:
resolution:
{
@@ -4401,6 +4510,12 @@ packages:
engines: { node: ">=14.17" }
hasBin: true
+ uc.micro@2.1.0:
+ resolution:
+ {
+ integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==
+ }
+
ufo@1.6.1:
resolution:
{
@@ -4419,6 +4534,13 @@ packages:
integrity: sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==
}
+ unicorn-magic@0.3.0:
+ resolution:
+ {
+ integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==
+ }
+ engines: { node: ">=18" }
+
universalify@2.0.1:
resolution:
{
@@ -5448,6 +5570,8 @@ snapshots:
transitivePeerDependencies:
- "@types/node"
+ "@sindresorhus/merge-streams@2.3.0": {}
+
"@svgr/babel-plugin-add-jsx-attribute@8.0.0(@babel/core@7.28.0)":
dependencies:
"@babel/core": 7.28.0
@@ -5691,6 +5815,17 @@ snapshots:
dependencies:
"@testing-library/dom": 10.4.0
+ "@ts-ast-parser/comment@0.2.0":
+ dependencies:
+ tslib: 2.8.1
+
+ "@ts-ast-parser/core@0.8.0(typescript@5.8.3)":
+ dependencies:
+ "@ts-ast-parser/comment": 0.2.0
+ globby: 14.1.0
+ tslib: 2.8.1
+ typescript: 5.8.3
+
"@tsconfig/node10@1.0.11": {}
"@tsconfig/node12@1.0.11": {}
@@ -5707,12 +5842,29 @@ snapshots:
dependencies:
"@types/deep-eql": 4.0.2
+ "@types/debug@4.1.12":
+ dependencies:
+ "@types/ms": 2.1.0
+ optional: true
+
"@types/deep-eql@4.0.2": {}
"@types/estree@1.0.8": {}
"@types/json-schema@7.0.15": {}
+ "@types/linkify-it@5.0.0": {}
+
+ "@types/markdown-it@14.1.2":
+ dependencies:
+ "@types/linkify-it": 5.0.0
+ "@types/mdurl": 2.0.0
+
+ "@types/mdurl@2.0.0": {}
+
+ "@types/ms@2.1.0":
+ optional: true
+
"@types/node@18.19.121":
dependencies:
undici-types: 5.26.5
@@ -6371,6 +6523,15 @@ snapshots:
globals@16.3.0: {}
+ globby@14.1.0:
+ dependencies:
+ "@sindresorhus/merge-streams": 2.3.0
+ fast-glob: 3.3.3
+ ignore: 7.0.5
+ path-type: 6.0.0
+ slash: 5.1.0
+ unicorn-magic: 0.3.0
+
graceful-fs@4.2.11: {}
graphemer@1.4.0: {}
@@ -6586,6 +6747,10 @@ snapshots:
lines-and-columns@1.2.4: {}
+ linkify-it@5.0.0:
+ dependencies:
+ uc.micro: 2.1.0
+
lint-staged@16.1.4:
dependencies:
chalk: 5.4.1
@@ -6656,8 +6821,19 @@ snapshots:
make-error@1.3.6: {}
+ markdown-it@14.1.0:
+ dependencies:
+ argparse: 2.0.1
+ entities: 4.5.0
+ linkify-it: 5.0.0
+ mdurl: 2.0.0
+ punycode.js: 2.3.1
+ uc.micro: 2.1.0
+
marked@16.4.1: {}
+ mdurl@2.0.0: {}
+
merge-stream@2.0.0: {}
merge2@1.4.1: {}
@@ -6777,6 +6953,8 @@ snapshots:
path-type@4.0.0: {}
+ path-type@6.0.0: {}
+
pathe@2.0.3: {}
pathval@2.0.1: {}
@@ -6823,6 +7001,8 @@ snapshots:
ansi-styles: 5.2.0
react-is: 17.0.2
+ punycode.js@2.3.1: {}
+
punycode@2.3.1: {}
quansync@0.2.11: {}
@@ -6976,6 +7156,8 @@ snapshots:
signal-exit@4.1.0: {}
+ slash@5.1.0: {}
+
slice-ansi@5.0.0:
dependencies:
ansi-styles: 6.2.1
@@ -7197,12 +7379,16 @@ snapshots:
typescript@5.8.3: {}
+ uc.micro@2.1.0: {}
+
ufo@1.6.1: {}
undici-types@5.26.5: {}
undici-types@7.10.0: {}
+ unicorn-magic@0.3.0: {}
+
universalify@2.0.1: {}
update-browserslist-db@1.1.3(browserslist@4.25.1):
@@ -7288,7 +7474,7 @@ snapshots:
terser: 5.43.1
yaml: 2.8.0
- vitest@3.2.4(@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)(yaml@2.8.0):
dependencies:
"@types/chai": 5.2.2
"@vitest/expect": 3.2.4
@@ -7314,6 +7500,7 @@ snapshots:
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)
why-is-node-running: 2.3.0
optionalDependencies:
+ "@types/debug": 4.1.12
"@types/node": 24.2.0
jsdom: 26.1.0
transitivePeerDependencies:
diff --git a/public/generated/js-docs/Grid.json b/public/generated/js-docs/Grid.json
index a98d07cc..f170bdce 100644
--- a/public/generated/js-docs/Grid.json
+++ b/public/generated/js-docs/Grid.json
@@ -1,150 +1,204 @@
{
+ "description": [
+ {
+ "content": "Renders data with many rows and columns.
\n"
+ }
+ ],
"filePath": "lib/components/grid/Grid.tsx",
"name": "Grid",
"props": {
"className": {
- "description": "CSS class name.",
- "html": "className ? : string | undefined
",
- "infos": [],
+ "description": [
+ {
+ "content": "CSS class name.
\n"
+ }
+ ],
+ "html": "className ? : string
",
"name": "className",
- "required": false,
- "warnings": []
+ "required": false
},
"dir": {
- "description": "Corresponds to the HTML dir attribute:\nhttps://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/dir",
- "html": "dir ? : string | undefined
",
- "infos": [],
+ "description": [
+ {
+ "content": "Indicates the directionality of grid cells.
\n"
+ },
+ {
+ "content": "See HTML dir global attribute for more information.
\n",
+ "intent": "primary"
+ }
+ ],
+ "html": "dir ? : string
",
"name": "dir",
- "required": false,
- "warnings": []
+ "required": false
},
"style": {
- "description": "Optional CSS properties.\nThe grid of cells will fill the height and width defined by this style.",
- "html": "style ? : CSSProperties | undefined
",
- "infos": [],
+ "description": [
+ {
+ "content": "Optional CSS properties.\nThe grid of cells will fill the height and width defined by this style.
\n"
+ }
+ ],
+ "html": "style ? : CSSProperties
",
"name": "style",
- "required": false,
- "warnings": []
+ "required": false
},
"children": {
- "description": "Additional content to be rendered within the grid (above cells).\nThis property can be used to render things like overlays or tooltips.",
+ "description": [
+ {
+ "content": "Additional content to be rendered within the grid (above cells).\nThis property can be used to render things like overlays or tooltips.
\n"
+ }
+ ],
"html": "children ? : ReactNode
",
- "infos": [],
"name": "children",
- "required": false,
- "warnings": []
+ "required": false
},
"cellComponent": {
- "description": "React component responsible for rendering a cell. This component will receive an index and style prop by default.\nAdditionally it will receive prop values passed to cellProps.",
- "html": "cellComponent : ( props : { ariaAttributes : { \"aria-colindex\" : number ; role : \"gridcell\" ; } ; columnIndex : number ; rowIndex : number ; style : CSSProperties ; } & CellProps ) => ReactNode
",
- "infos": [
- "The prop types for this component are exported as CellComponentProps"
+ "description": [
+ {
+ "content": "React component responsible for rendering a cell.
\n"
+ },
+ {
+ "content": "This component will receive an index and style prop by default.\nAdditionally it will receive prop values passed to cellProps.
\n"
+ },
+ {
+ "content": "The prop types for this component are exported as CellComponentProps
\n",
+ "intent": "primary"
+ }
],
+ "html": "cellComponent : ( props : { ariaAttributes : { \"aria-colindex\" : number ; role : \"gridcell\" ; } ; columnIndex : number ; rowIndex : number ; style : CSSProperties ; } & CellProps ) => ReactNode
",
"name": "cellComponent",
- "required": true,
- "warnings": []
+ "required": true
},
"cellProps": {
- "description": "Additional props to be passed to the cell-rendering component.\nGrid will automatically re-render cells when values in this object change.",
+ "description": [
+ {
+ "content": "Additional props to be passed to the cell-rendering component.\nGrid will automatically re-render cells when values in this object change.
\n"
+ },
+ {
+ "content": "This object must not contain ariaAttributes, columnIndex, rowIndex, or style props.
\n",
+ "intent": "warning"
+ }
+ ],
"html": "cellProps : CellProps
",
- "infos": [],
"name": "cellProps",
- "required": true,
- "warnings": [
- "This object must not contain ariaAttributes, columnIndex, rowIndex, or style props."
- ]
+ "required": true
},
"columnCount": {
- "description": "Number of columns to be rendered in the grid.",
+ "description": [
+ {
+ "content": "Number of columns to be rendered in the grid.
\n"
+ }
+ ],
"html": "columnCount : number
",
- "infos": [],
"name": "columnCount",
- "required": true,
- "warnings": []
+ "required": true
},
"columnWidth": {
- "description": "Column width; the following formats are supported: • number of pixels (number) • percentage of the grid's current width (string) • function that returns the row width (in pixels) given an index and cellProps",
+ "description": [
+ {
+ "content": "Column width; the following formats are supported:
\n\nnumber of pixels (number) \npercentage of the grid's current width (string) \nfunction that returns the row width (in pixels) given an index and cellProps \n \n"
+ }
+ ],
"html": "columnWidth : string | number | ( ( index : number , cellProps : CellProps ) => number )
",
- "infos": [],
"name": "columnWidth",
- "required": true,
- "warnings": []
+ "required": true
},
"defaultHeight": {
- "description": "Default height of grid for initial render.\nThis value is important for server rendering.",
- "html": "defaultHeight ? : number | undefined = 0
",
- "infos": [],
+ "description": [
+ {
+ "content": "Default height of grid for initial render.\nThis value is important for server rendering.
\n"
+ }
+ ],
+ "html": "defaultHeight ? : number = 0
",
"name": "defaultHeight",
- "required": false,
- "warnings": []
+ "required": false
},
"defaultWidth": {
- "description": "Default width of grid for initial render.\nThis value is important for server rendering.",
- "html": "defaultWidth ? : number | undefined = 0
",
- "infos": [],
+ "description": [
+ {
+ "content": "Default width of grid for initial render.\nThis value is important for server rendering.
\n"
+ }
+ ],
+ "html": "defaultWidth ? : number = 0
",
"name": "defaultWidth",
- "required": false,
- "warnings": []
+ "required": false
},
"gridRef": {
- "description": "Ref used to interact with this component's imperative API. This API has imperative methods for scrolling and a getter for the outermost DOM element.",
- "html": "gridRef ? : Ref < { readonly element : HTMLDivElement | null ; scrollToCell ( config : { behavior ? : \"auto\" | \"instant\" | \"smooth\" | undefined ; columnAlign ? : \"auto\" | \"center\" | \"end\" | \"smart\" | \"start\" | undefined ; columnIndex : number ; rowAlign ? : \"auto\" | . . . 4 more . . . | undefined ; rowIndex : number ; } ) : void ; scrollToColumn ( config : . . .
",
- "infos": [
- "The useGridRef and useGridCallbackRef hooks are exported for convenience use in TypeScript projects."
+ "description": [
+ {
+ "content": "Imperative Grid API.
\n"
+ },
+ {
+ "content": "The useGridRef and useGridCallbackRef hooks are exported for convenience use in TypeScript projects.
\n",
+ "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 : . . .
",
"name": "gridRef",
- "required": false,
- "warnings": []
+ "required": false
},
"onCellsRendered": {
- "description": "Callback notified when the range of rendered cells changes.",
- "html": "onCellsRendered ? : ( ( visibleCells : { columnStartIndex : number ; columnStopIndex : number ; rowStartIndex : number ; rowStopIndex : number ; } , allCells : { columnStartIndex : number ; columnStopIndex : number ; rowStartIndex : number ; rowStopIndex : number ; } ) => void ) | undefined
",
- "infos": [],
+ "description": [
+ {
+ "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 )
",
"name": "onCellsRendered",
- "required": false,
- "warnings": []
+ "required": false
},
"onResize": {
- "description": "Callback notified when the Grid's outermost HTMLElement resizes.\nThis may be used to (re)scroll a cell into view.",
- "html": "onResize ? : ( ( size : { height : number ; width : number ; } , prevSize : { height : number ; width : number ; } ) => void ) | undefined
",
- "infos": [],
+ "description": [
+ {
+ "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 )
",
"name": "onResize",
- "required": false,
- "warnings": []
+ "required": false
},
"overscanCount": {
- "description": "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.",
- "html": "overscanCount ? : number | undefined = 3
",
- "infos": [],
+ "description": [
+ {
+ "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
",
"name": "overscanCount",
- "required": false,
- "warnings": []
+ "required": false
},
"rowCount": {
- "description": "Number of rows to be rendered in the grid.",
+ "description": [
+ {
+ "content": "Number of rows to be rendered in the grid.
\n"
+ }
+ ],
"html": "rowCount : number
",
- "infos": [],
"name": "rowCount",
- "required": true,
- "warnings": []
+ "required": true
},
"rowHeight": {
- "description": "Row height; the following formats are supported: • number of pixels (number) • percentage of the grid's current height (string) • function that returns the row height (in pixels) given an index and cellProps",
+ "description": [
+ {
+ "content": "Row height; the following formats are supported:
\n\nnumber of pixels (number) \npercentage of the grid's current height (string) \nfunction that returns the row height (in pixels) given an index and cellProps \n \n"
+ }
+ ],
"html": "rowHeight : string | number | ( ( index : number , cellProps : CellProps ) => number )
",
- "infos": [],
"name": "rowHeight",
- "required": true,
- "warnings": []
+ "required": true
},
"tagName": {
- "description": "Can be used to override the root HTML element rendered by the List component.\nThe default value is \"div\", meaning that List renders an HTMLDivElement as its root.",
- "html": "tagName ? : keyof IntrinsicElements | undefined = \"div\" as TagName
",
- "infos": [],
+ "description": [
+ {
+ "content": "Can be used to override the root HTML element rendered by the List component.\nThe default value is "div", meaning that List renders an HTMLDivElement as its root.
\n"
+ },
+ {
+ "content": "In most use cases the default ARIA roles are sufficient and this prop is not needed.
\n",
+ "intent": "warning"
+ }
+ ],
+ "html": "tagName ? : keyof IntrinsicElements = \"div\" as TagName
",
"name": "tagName",
- "required": false,
- "warnings": [
- "In most use cases the default ARIA roles are sufficient and this prop is not needed."
- ]
+ "required": false
}
}
}
\ No newline at end of file
diff --git a/public/generated/js-docs/GridImperativeAPI.json b/public/generated/js-docs/GridImperativeAPI.json
new file mode 100644
index 00000000..a199d027
--- /dev/null
+++ b/public/generated/js-docs/GridImperativeAPI.json
@@ -0,0 +1,42 @@
+{
+ "description": [
+ {
+ "content": "Ref used to interact with this component's imperative API.\nThis API has imperative methods for scrolling and a getter for the outermost DOM element.
\n"
+ },
+ {
+ "content": "The useGridRef and useGridCallbackRef hooks are exported for convenience use in TypeScript projects.
\n",
+ "intent": "primary"
+ }
+ ],
+ "filePath": "lib/components/grid/types.ts",
+ "methods": [
+ {
+ "description": [
+ {
+ "content": "Scrolls the grid so that the specified row and column are visible.
\n"
+ }
+ ],
+ "html": "scrollToCell : ( {
\n behavior ,
\n columnAlign ,
\n columnIndex ,
\n rowAlign ,
\n rowIndex
\n } : {
\n behavior ? : \"auto\" | \"instant\" | \"smooth\" ;
\n columnAlign ? : \"auto\" | \"center\" | \"end\" | \"smart\" | \"start\" ;
\n columnIndex : number ;
\n rowAlign ? : \"auto\" | \"center\" | \"end\" | \"smart\" | \"start\" ;
\n rowIndex : number ;
\n } ) => void ;
",
+ "name": "scrollToCell"
+ },
+ {
+ "description": [
+ {
+ "content": "Scrolls the grid so that the specified column is visible.
\n"
+ }
+ ],
+ "html": "scrollToColumn : ( {
\n align ,
\n behavior ,
\n index
\n } : {
\n align ? : \"auto\" | \"center\" | \"end\" | \"smart\" | \"start\" ;
\n behavior ? : \"auto\" | \"instant\" | \"smooth\" ;
\n index : number ;
\n } ) => void ;
",
+ "name": "scrollToColumn"
+ },
+ {
+ "description": [
+ {
+ "content": "Scrolls the grid so that the specified row is visible.
\n"
+ }
+ ],
+ "html": "scrollToRow : ( {
\n align ,
\n behavior ,
\n index
\n } : {
\n align ? : \"auto\" | \"center\" | \"end\" | \"smart\" | \"start\" ;
\n behavior ? : \"auto\" | \"instant\" | \"smooth\" ;
\n index : number ;
\n } ) => void ;
",
+ "name": "scrollToRow"
+ }
+ ],
+ "name": "GridImperativeAPI"
+}
\ No newline at end of file
diff --git a/public/generated/js-docs/List.json b/public/generated/js-docs/List.json
index 497fc13e..e2a99107 100644
--- a/public/generated/js-docs/List.json
+++ b/public/generated/js-docs/List.json
@@ -1,120 +1,167 @@
{
+ "description": [
+ {
+ "content": "Renders data with many rows.
\n"
+ }
+ ],
"filePath": "lib/components/list/List.tsx",
"name": "List",
"props": {
"className": {
- "description": "CSS class name.",
- "html": "className ? : string | undefined
",
- "infos": [],
+ "description": [
+ {
+ "content": "CSS class name.
\n"
+ }
+ ],
+ "html": "className ? : string
",
"name": "className",
- "required": false,
- "warnings": []
+ "required": false
},
"style": {
- "description": "Optional CSS properties.\nThe list of rows will fill the height defined by this style.",
- "html": "style ? : CSSProperties | undefined
",
- "infos": [],
+ "description": [
+ {
+ "content": "Optional CSS properties.\nThe list of rows will fill the height defined by this style.
\n"
+ }
+ ],
+ "html": "style ? : CSSProperties
",
"name": "style",
- "required": false,
- "warnings": []
+ "required": false
},
"children": {
- "description": "Additional content to be rendered within the list (above cells).\nThis property can be used to render things like overlays or tooltips.",
+ "description": [
+ {
+ "content": "Additional content to be rendered within the list (above cells).\nThis property can be used to render things like overlays or tooltips.
\n"
+ }
+ ],
"html": "children ? : ReactNode
",
- "infos": [],
"name": "children",
- "required": false,
- "warnings": []
+ "required": false
},
"defaultHeight": {
- "description": "Default height of list for initial render.\nThis value is important for server rendering.",
- "html": "defaultHeight ? : number | undefined = 0
",
- "infos": [],
+ "description": [
+ {
+ "content": "Default height of list for initial render.\nThis value is important for server rendering.
\n"
+ }
+ ],
+ "html": "defaultHeight ? : number = 0
",
"name": "defaultHeight",
- "required": false,
- "warnings": []
+ "required": false
},
"listRef": {
- "description": "Ref used to interact with this component's imperative API. This API has imperative methods for scrolling and a getter for the outermost DOM element.",
- "html": "listRef ? : Ref < { readonly element : HTMLDivElement | null ; scrollToRow ( config : { align ? : \"auto\" | \"center\" | \"end\" | \"smart\" | \"start\" | undefined ; behavior ? : \"auto\" | \"instant\" | \"smooth\" | undefined ; index : number ; } ) : void ; } > | undefined
",
- "infos": [
- "The useListRef and useListCallbackRef hooks are exported for convenience use in TypeScript projects."
+ "description": [
+ {
+ "content": "Ref used to interact with this component's imperative API.
\n"
+ },
+ {
+ "content": "This API has imperative methods for scrolling and a getter for the outermost DOM element.
\n"
+ },
+ {
+ "content": "The useListRef and useListCallbackRef hooks are exported for convenience use in TypeScript projects.
\n",
+ "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
",
"name": "listRef",
- "required": false,
- "warnings": []
+ "required": false
},
"onResize": {
- "description": "Callback notified when the List's outermost HTMLElement resizes.\nThis may be used to (re)scroll a row into view.",
- "html": "onResize ? : ( ( size : { height : number ; width : number ; } , prevSize : { height : number ; width : number ; } ) => void ) | undefined
",
- "infos": [],
+ "description": [
+ {
+ "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 )
",
"name": "onResize",
- "required": false,
- "warnings": []
+ "required": false
},
"onRowsRendered": {
- "description": "Callback notified when the range of visible rows changes.",
- "html": "onRowsRendered ? : ( ( visibleRows : { startIndex : number ; stopIndex : number ; } , allRows : { startIndex : number ; stopIndex : number ; } ) => void ) | undefined
",
- "infos": [],
+ "description": [
+ {
+ "content": "Callback notified when the range of visible rows changes.
\n"
+ }
+ ],
+ "html": "onRowsRendered ? : ( ( visibleRows : { startIndex : number ; stopIndex : number ; } , allRows : { startIndex : number ; stopIndex : number ; } ) => void )
",
"name": "onRowsRendered",
- "required": false,
- "warnings": []
+ "required": false
},
"overscanCount": {
- "description": "How many additional rows to render outside of the visible area.\nThis can reduce visual flickering near the edges of a list when scrolling.",
- "html": "overscanCount ? : number | undefined = 3
",
- "infos": [],
+ "description": [
+ {
+ "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
",
"name": "overscanCount",
- "required": false,
- "warnings": []
+ "required": false
},
"rowComponent": {
- "description": "React component responsible for rendering a row. This component will receive an index and style prop by default.\nAdditionally it will receive prop values passed to rowProps.",
- "html": "rowComponent : ( props : { ariaAttributes : { \"aria-posinset\" : number ; \"aria-setsize\" : number ; role : \"listitem\" ; } ; index : number ; style : CSSProperties ; } & RowProps ) => ReactNode
",
- "infos": [
- "The prop types for this component are exported as RowComponentProps"
+ "description": [
+ {
+ "content": "React component responsible for rendering a row.
\n"
+ },
+ {
+ "content": "This component will receive an index and style prop by default.\nAdditionally it will receive prop values passed to rowProps.
\n"
+ },
+ {
+ "content": "The prop types for this component are exported as RowComponentProps
\n",
+ "intent": "primary"
+ }
],
+ "html": "rowComponent : ( props : { ariaAttributes : { \"aria-posinset\" : number ; \"aria-setsize\" : number ; role : \"listitem\" ; } ; index : number ; style : CSSProperties ; } & RowProps ) => ReactNode
",
"name": "rowComponent",
- "required": true,
- "warnings": []
+ "required": true
},
"rowCount": {
- "description": "Number of items to be rendered in the list.",
+ "description": [
+ {
+ "content": "Number of items to be rendered in the list.
\n"
+ }
+ ],
"html": "rowCount : number
",
- "infos": [],
"name": "rowCount",
- "required": true,
- "warnings": []
+ "required": true
},
"rowHeight": {
- "description": "Row height; the following formats are supported: • number of pixels (number) • percentage of the grid's current height (string) • function that returns the row height (in pixels) given an index and cellProps • dynamic row height cache returned by the useDynamicRowHeight hook",
+ "description": [
+ {
+ "content": "Row height; the following formats are supported:
\n\nnumber of pixels (number) \npercentage of the grid's current height (string) \nfunction that returns the row height (in pixels) given an index and cellProps \ndynamic row height cache returned by the useDynamicRowHeight hook \n \n"
+ },
+ {
+ "content": "Dynamic row heights are not as efficient as predetermined sizes.\nIt's recommended to provide your own height values if they can be determined ahead of time.
\n",
+ "intent": "warning"
+ }
+ ],
"html": "rowHeight : string | number | DynamicRowHeight | ( ( index : number , cellProps : RowProps ) => number )
",
- "infos": [],
"name": "rowHeight",
- "required": true,
- "warnings": [
- "Dynamic row heights are not as efficient as predetermined sizes.\nIt's recommended to provide your own height values if they can be determined ahead of time."
- ]
+ "required": true
},
"rowProps": {
- "description": "Additional props to be passed to the row-rendering component.\nList will automatically re-render rows when values in this object change.",
+ "description": [
+ {
+ "content": "Additional props to be passed to the row-rendering component.\nList will automatically re-render rows when values in this object change.
\n"
+ },
+ {
+ "content": "This object must not contain ariaAttributes, index, or style props.
\n",
+ "intent": "warning"
+ }
+ ],
"html": "rowProps : RowProps
",
- "infos": [],
"name": "rowProps",
- "required": true,
- "warnings": [
- "This object must not contain ariaAttributes, index, or style props."
- ]
+ "required": true
},
"tagName": {
- "description": "Can be used to override the root HTML element rendered by the List component.\nThe default value is \"div\", meaning that List renders an HTMLDivElement as its root.",
- "html": "tagName ? : keyof IntrinsicElements | undefined = \"div\" as TagName
",
- "infos": [],
+ "description": [
+ {
+ "content": "Can be used to override the root HTML element rendered by the List component.\nThe default value is "div", meaning that List renders an HTMLDivElement as its root.
\n"
+ },
+ {
+ "content": "In most use cases the default ARIA roles are sufficient and this prop is not needed.
\n",
+ "intent": "warning"
+ }
+ ],
+ "html": "tagName ? : keyof IntrinsicElements = \"div\" as TagName
",
"name": "tagName",
- "required": false,
- "warnings": [
- "In most use cases the default ARIA roles are sufficient and this prop is not needed."
- ]
+ "required": false
}
}
}
\ No newline at end of file
diff --git a/public/generated/js-docs/ListImperativeAPI.json b/public/generated/js-docs/ListImperativeAPI.json
new file mode 100644
index 00000000..8be088ee
--- /dev/null
+++ b/public/generated/js-docs/ListImperativeAPI.json
@@ -0,0 +1,24 @@
+{
+ "description": [
+ {
+ "content": "Imperative List API.
\n"
+ },
+ {
+ "content": "The useListRef and useListCallbackRef hooks are exported for convenience use in TypeScript projects.
\n",
+ "intent": "primary"
+ }
+ ],
+ "filePath": "lib/components/list/types.ts",
+ "methods": [
+ {
+ "description": [
+ {
+ "content": "Scrolls the list so that the specified row is visible.
\n"
+ }
+ ],
+ "html": "scrollToRow : ( {
\n align ,
\n behavior ,
\n index
\n } : {
\n align ? : \"auto\" | \"center\" | \"end\" | \"smart\" | \"start\" ;
\n behavior ? : \"auto\" | \"instant\" | \"smooth\" ;
\n index : number ;
\n } ) => void ;
",
+ "name": "scrollToRow"
+ }
+ ],
+ "name": "ListImperativeAPI"
+}
\ No newline at end of file
diff --git a/scripts/compile-code-snippets.ts b/scripts/compile-code-snippets.ts
index 7a31dff7..6cf8c12b 100644
--- a/scripts/compile-code-snippets.ts
+++ b/scripts/compile-code-snippets.ts
@@ -2,7 +2,7 @@ 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/trimExcludedText.ts";
+import { trimExcludedText } from "./utils/code-snippets/trimExcludedText.ts";
async function run() {
const { files, outputDir } = await initialize({
@@ -14,8 +14,6 @@ async function run() {
const exampleFiles = files.filter((file) => file.includes(".example."));
for (const file of exampleFiles) {
- console.debug("Extracting", file);
-
const buffer = await readFile(file);
let rawText = buffer.toString();
diff --git a/scripts/compile-docs.ts b/scripts/compile-docs.ts
index d4381195..57af3783 100644
--- a/scripts/compile-docs.ts
+++ b/scripts/compile-docs.ts
@@ -1,158 +1,16 @@
-import { readFile, writeFile } from "node:fs/promises";
-import { join, relative } from "node:path";
-import { cwd } from "node:process";
-import { withCustomConfig, type PropItem } from "react-docgen-typescript";
-import type { ComponentMetadata } from "../src/types.ts";
-import { formatDescriptionText } from "./utils/formatDescriptionText.ts";
-import { getPropTypeText } from "./utils/getPropTypeText.ts";
-import { initialize } from "./utils/initialize.ts";
-import { propsToTable } from "./utils/propsToTable.ts";
-import { syntaxHighlight } from "./utils/syntax-highlight.ts";
-import { insertPropsMarkdown } from "./utils/insertPropsMarkdown.ts";
-
-const parser = withCustomConfig("./tsconfig.json", {
- savePropValueAsString: true,
- shouldExtractLiteralValuesFromEnum: true,
- shouldExtractValuesFromUnion: true
-});
-
-const TOKEN_TO_REPLACE = "TOKEN_TO_REPLACE";
+import { compileComponents } from "./utils/docs/compileComponents.ts";
+import { compileImperativeHandles } from "./utils/docs/compileImperativeHandles.ts";
async function run() {
- const { files, outputDir } = await initialize({
- fileExtensions: [".ts", ".tsx"],
- fileFilter: (file) =>
- file.endsWith("/Grid.tsx") || file.endsWith("/List.tsx"),
- inputPath: ["lib", "components"],
+ await compileComponents({
+ componentNames: ["grid/Grid.tsx", "list/List.tsx"],
outputDirName: "js-docs"
});
- const markdownPath = join(cwd(), "README.md");
-
- let markdown = await readFile(markdownPath, { encoding: "utf-8" });
-
- for (const file of files) {
- console.debug("Parsing", file);
-
- const parsed = parser.parse(file);
-
- for (const component of parsed) {
- // Convert to local paths
- component.filePath = relative(cwd(), file);
-
- // 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 = {
- 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} `
- );
-
- let description = "";
- const infos: string[] = [];
- const warnings: string[] = [];
-
- if (prop.description.includes("⚠️")) {
- const pieces = prop.description.split("⚠️");
- description = pieces[0];
- for (let index = 1; index < pieces.length; index++) {
- warnings.push(pieces[index]);
- }
- } else if (prop.description.includes("ℹ️")) {
- const pieces = prop.description.split("ℹ️");
- description = pieces[0];
- for (let index = 1; index < pieces.length; index++) {
- infos.push(pieces[index]);
- }
- } else {
- description = prop.description;
- }
-
- componentMetadata.props[name] = {
- description: formatDescriptionText(description.trim()),
- html,
- infos: infos.map((info) => formatDescriptionText(info.trim())),
- name,
- required: prop.required,
- warnings: warnings.map((warning) =>
- formatDescriptionText(warning.trim())
- )
- };
- } 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);
- }
- }
-
- markdown = insertPropsMarkdown({
- componentMarkdown: await propsToTable(requiredProps),
- componentName: component.displayName,
- markdown,
- required: true
- });
-
- markdown = insertPropsMarkdown({
- componentMarkdown: await propsToTable(optionalProps),
- componentName: component.displayName,
- markdown,
- required: false
- });
- }
- }
- }
-
- await writeFile(markdownPath, markdown);
+ await compileImperativeHandles({
+ names: ["GridImperativeAPI", "ListImperativeAPI"],
+ outputDirName: "js-docs"
+ });
}
run();
diff --git a/scripts/utils/trimExcludedText.ts b/scripts/utils/code-snippets/trimExcludedText.ts
similarity index 100%
rename from scripts/utils/trimExcludedText.ts
rename to scripts/utils/code-snippets/trimExcludedText.ts
diff --git a/scripts/utils/docs/compileComponent.ts b/scripts/utils/docs/compileComponent.ts
new file mode 100644
index 00000000..7b7c24e6
--- /dev/null
+++ b/scripts/utils/docs/compileComponent.ts
@@ -0,0 +1,110 @@
+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,
+ optionalPropsTable: await propsToTable(optionalProps),
+ requiredPropsTable: await propsToTable(requiredProps)
+ };
+}
diff --git a/scripts/utils/docs/compileComponents.ts b/scripts/utils/docs/compileComponents.ts
new file mode 100644
index 00000000..249c50ed
--- /dev/null
+++ b/scripts/utils/docs/compileComponents.ts
@@ -0,0 +1,60 @@
+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, optionalPropsTable, requiredPropsTable }) => {
+ markdown = insertPropsMarkdown({
+ componentMarkdown: requiredPropsTable,
+ componentName,
+ markdown,
+ required: true
+ });
+
+ markdown = insertPropsMarkdown({
+ componentMarkdown: optionalPropsTable,
+ componentName,
+ markdown,
+ required: false
+ });
+ })
+ )
+ );
+
+ await writeFile(markdownPath, markdown);
+}
diff --git a/scripts/utils/docs/compileImperativeHandle.ts b/scripts/utils/docs/compileImperativeHandle.ts
new file mode 100644
index 00000000..3e218e8a
--- /dev/null
+++ b/scripts/utils/docs/compileImperativeHandle.ts
@@ -0,0 +1,48 @@
+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
new file mode 100644
index 00000000..57fbdbe3
--- /dev/null
+++ b/scripts/utils/docs/compileImperativeHandles.ts
@@ -0,0 +1,45 @@
+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
new file mode 100644
index 00000000..9e047cf1
--- /dev/null
+++ b/scripts/utils/docs/formatDescriptionText.ts
@@ -0,0 +1,11 @@
+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/getPropTypeText.ts b/scripts/utils/docs/getPropTypeText.ts
similarity index 93%
rename from scripts/utils/getPropTypeText.ts
rename to scripts/utils/docs/getPropTypeText.ts
index 51ce874e..f888e61a 100644
--- a/scripts/utils/getPropTypeText.ts
+++ b/scripts/utils/docs/getPropTypeText.ts
@@ -8,7 +8,7 @@ export function getPropTypeText(prop: PropItem) {
// 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-window/issues/875
+ // See github.com/bvaughn/react-resizable-panels/issues/875
textToFormat = textToFormat.replace(
"ReactElement>",
"ReactNode"
diff --git a/scripts/utils/insertPropsMarkdown.ts b/scripts/utils/docs/insertPropsMarkdown.ts
similarity index 100%
rename from scripts/utils/insertPropsMarkdown.ts
rename to scripts/utils/docs/insertPropsMarkdown.ts
diff --git a/scripts/utils/docs/parseDescription.ts b/scripts/utils/docs/parseDescription.ts
new file mode 100644
index 00000000..daaad5e4
--- /dev/null
+++ b/scripts/utils/docs/parseDescription.ts
@@ -0,0 +1,55 @@
+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} `, "
");
+ }
+ }
+ }
+
+ sections.push({
+ content,
+ intent
+ });
+ }
+
+ return sections;
+}
+
+const INTENT_FLAGS = {
+ "❌": "danger",
+ "NOTE:": "none",
+ ℹ️: "primary",
+ "✅": "success",
+ "⚠️": "warning"
+};
diff --git a/scripts/utils/propsToTable.ts b/scripts/utils/docs/propsToTable.ts
similarity index 54%
rename from scripts/utils/propsToTable.ts
rename to scripts/utils/docs/propsToTable.ts
index 1a24e9a6..7cd96ae6 100644
--- a/scripts/utils/propsToTable.ts
+++ b/scripts/utils/docs/propsToTable.ts
@@ -24,21 +24,25 @@ const TABLE_TAG_STOP = `
`;
export async function propsToTable(props: PropItem[]) {
- const htmlStrings = [TABLE_TAG_START];
+ const htmlStrings = [];
- for (const prop of props) {
- const type = getPropTypeText(prop);
+ if (props.length > 0) {
+ htmlStrings.push(TABLE_TAG_START);
- const description = await marked(prop.description);
+ for (const prop of props) {
+ const type = getPropTypeText(prop);
- htmlStrings.push(
- PROP_ROW.replace("[[name]]", prop.name)
- .replace("[[type]]", type)
- .replace("[[description]]", description)
- );
- }
+ 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);
+ htmlStrings.push(TABLE_TAG_STOP);
+ }
return htmlStrings.join("");
}
diff --git a/scripts/utils/formatDescriptionText.ts b/scripts/utils/formatDescriptionText.ts
deleted file mode 100644
index 61d56176..00000000
--- a/scripts/utils/formatDescriptionText.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-export function formatDescriptionText(text: string) {
- return text
- .replaceAll("\n- ", " • ")
- .replaceAll("\n\n", " ")
- .replaceAll(/`([^`]+)`/g, "$1");
-}
diff --git a/scripts/utils/syntax-highlight.ts b/scripts/utils/syntax-highlight.ts
index 768289a1..872f951e 100644
--- a/scripts/utils/syntax-highlight.ts
+++ b/scripts/utils/syntax-highlight.ts
@@ -16,7 +16,7 @@ type Token = {
value: string;
};
-type Language = "HTML" | "JS" | "JSX" | "TS" | "TSX";
+export type Language = "HTML" | "JS" | "JSX" | "TS" | "TSX";
type State = {
parsedTokens: Token[];
@@ -140,7 +140,7 @@ async function parser(
}
);
- const maxPosition = code.length - 1;
+ const maxPosition = code.length;
if (characterIndex < maxPosition) {
// No style applied on the trailing text.
diff --git a/src/components/DocsSection.tsx b/src/components/DocsSection.tsx
new file mode 100644
index 00000000..c6e3ca6d
--- /dev/null
+++ b/src/components/DocsSection.tsx
@@ -0,0 +1,34 @@
+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/handles/ImperativeHandle.tsx b/src/components/handles/ImperativeHandle.tsx
new file mode 100644
index 00000000..463ca7dc
--- /dev/null
+++ b/src/components/handles/ImperativeHandle.tsx
@@ -0,0 +1,38 @@
+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
new file mode 100644
index 00000000..a552bb54
--- /dev/null
+++ b/src/components/handles/ImperativeHandleMethod.tsx
@@ -0,0 +1,19 @@
+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
new file mode 100644
index 00000000..1a0dc498
--- /dev/null
+++ b/src/components/props/ComponentProp.tsx
@@ -0,0 +1,19 @@
+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
index 13bcbf85..c26ba9f7 100644
--- a/src/components/props/ComponentProps.tsx
+++ b/src/components/props/ComponentProps.tsx
@@ -1,12 +1,13 @@
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";
-import { useMemo } from "react";
-import { processPropsJSON } from "../../utils/processPropsJSON";
export function ComponentProps({
json,
@@ -21,18 +22,19 @@ export function ComponentProps({
);
return (
- <>
+
-
+
+
- >
+
);
}
diff --git a/src/components/props/ComponentPropsSection.tsx b/src/components/props/ComponentPropsSection.tsx
index 6af93ac2..5fb62563 100644
--- a/src/components/props/ComponentPropsSection.tsx
+++ b/src/components/props/ComponentPropsSection.tsx
@@ -1,7 +1,6 @@
-import { Fragment } from "react/jsx-runtime";
import type { ComponentPropMetadata } from "../../types";
-import { Callout } from "../Callout";
-import { Code } from "../code/Code";
+import { Box } from "../Box";
+import { ComponentProp } from "./ComponentProp";
export function ComponentPropsSection({
header,
@@ -15,49 +14,13 @@ export function ComponentPropsSection({
}
return (
-
-
{header}
-
+
+ {header}
+
{props.map((prop) => (
-
-
-
-
-
-
- {prop.infos.map((info, index) => (
-
- {info}
-
- ))}
- {prop.warnings.map((warning, index) => (
-
- {warning}
-
- ))}
-
-
+
))}
-
+
);
}
diff --git a/src/nav/Nav.tsx b/src/nav/Nav.tsx
index d60d9a6b..ec4f7847 100644
--- a/src/nav/Nav.tsx
+++ b/src/nav/Nav.tsx
@@ -9,9 +9,10 @@ export function Nav() {
Fixed row heights
Variable row heights
Dynamic row heights
- Imperative methods
+ Scroll to row
ARIA roles
- Props and API
+ List props
+ Imperative handle
Tabular data
@@ -19,9 +20,10 @@ export function Nav() {
Rendering a grid
- Imperative methods
+ Scroll to cells
ARIA roles
- Props and API
+ Grid props
+ Imperative handle
Right to left content
diff --git a/src/routes.ts b/src/routes.ts
index 15d7f78c..32c16334 100644
--- a/src/routes.ts
+++ b/src/routes.ts
@@ -18,10 +18,11 @@ export const routes = {
"/list/dynamic-row-height": lazy(
() => import("./routes/list/DynamicRowHeightsRoute")
),
- "/list/imperative-methods": lazy(
+ "/list/scroll-to-row": lazy(() => import("./routes/list/ScrollToRowRoute")),
+ "/list/props": lazy(() => import("./routes/list/PropsRoute")),
+ "/list/imperative-handle": lazy(
() => import("./routes/list/ImperativeApiRoute")
),
- "/list/props": lazy(() => import("./routes/list/PropsRoute")),
"/list/aria-roles": lazy(() => import("./routes/list/AriaRolesRoute")),
"/list/tabular-data": lazy(() => import("./routes/tables/TabularDataRoute")),
"/list/tabular-data-aria-roles": lazy(
@@ -37,9 +38,10 @@ export const routes = {
),
"/grid/rtl-grids": lazy(() => import("./routes/grid/RTLGridsRoute")),
"/grid/props": lazy(() => import("./routes/grid/PropsRoute")),
- "/grid/imperative-methods": lazy(
- () => import("./routes/grid/ImperativeApiRoute")
+ "/grid/imperative-handle": lazy(
+ () => import("./routes/grid/ImperativeHandleRoute")
),
+ "/grid/scroll-to-cell": lazy(() => import("./routes/grid/ScrollToCellRoute")),
"/grid/aria-roles": lazy(() => import("./routes/grid/AriaRolesRoute")),
// Other
diff --git a/src/routes/grid/ImperativeHandleRoute.tsx b/src/routes/grid/ImperativeHandleRoute.tsx
new file mode 100644
index 00000000..dc967c78
--- /dev/null
+++ b/src/routes/grid/ImperativeHandleRoute.tsx
@@ -0,0 +1,36 @@
+import { html as usePanelCallbackRefHTML } from "../../../public/generated/code-snippets/useGridCallbackRef.json";
+import { html as usePanelRefHTML } 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 type { ImperativeHandleMetadata } from "../../types";
+
+export default function GridImperativeHandleRoute() {
+ return (
+
+
+ Hooks
+
+ The usePanelRef hook returns a{" "}
+
+ mutable ref object
+
+ .
+
+
+
+ And the usePanelCallbackRef hook returns a{" "}
+
+ ref callback function
+
+ . This is better when sharing the ref with another hook or component.
+
+
+
+ );
+}
diff --git a/src/routes/grid/PropsRoute.tsx b/src/routes/grid/PropsRoute.tsx
index 43d0f6f9..f011b8f4 100644
--- a/src/routes/grid/PropsRoute.tsx
+++ b/src/routes/grid/PropsRoute.tsx
@@ -1,11 +1,12 @@
import json from "../../../public/generated/js-docs/Grid.json";
import { Box } from "../../components/Box";
import { ComponentProps } from "../../components/props/ComponentProps";
+import type { ComponentMetadata } from "../../types";
export default function GridPropsRoute() {
return (
-
+
);
}
diff --git a/src/routes/grid/RenderingGridRoute.tsx b/src/routes/grid/RenderingGridRoute.tsx
index 61bad959..00763aa1 100644
--- a/src/routes/grid/RenderingGridRoute.tsx
+++ b/src/routes/grid/RenderingGridRoute.tsx
@@ -60,7 +60,7 @@ export default function RenderingGridRoute() {
-
+
);
}
diff --git a/src/routes/grid/ImperativeApiRoute.tsx b/src/routes/grid/ScrollToCellRoute.tsx
similarity index 99%
rename from src/routes/grid/ImperativeApiRoute.tsx
rename to src/routes/grid/ScrollToCellRoute.tsx
index af027136..874f70f8 100644
--- a/src/routes/grid/ImperativeApiRoute.tsx
+++ b/src/routes/grid/ScrollToCellRoute.tsx
@@ -45,7 +45,7 @@ const COLUMNS: Option[] = COLUMN_KEYS.map((key) => ({
value: key
})).sort((a, b) => a.label.localeCompare(b.label));
-export default function GridImperativeApiRoute() {
+export default function ScrollToCellRoute() {
const contacts = useContacts();
const titleOptions = useMemo[]>(() => {
diff --git a/src/routes/list/DynamicRowHeightsRoute.tsx b/src/routes/list/DynamicRowHeightsRoute.tsx
index e97c2213..be49157f 100644
--- a/src/routes/list/DynamicRowHeightsRoute.tsx
+++ b/src/routes/list/DynamicRowHeightsRoute.tsx
@@ -43,7 +43,7 @@ export default function DynamicRowHeightsRoute() {
recommended to provide your own height values if they can be determined
ahead of time.
-
+
);
}
diff --git a/src/routes/list/ImperativeApiRoute.tsx b/src/routes/list/ImperativeApiRoute.tsx
index 3be82bea..4d8eac2b 100644
--- a/src/routes/list/ImperativeApiRoute.tsx
+++ b/src/routes/list/ImperativeApiRoute.tsx
@@ -1,138 +1,36 @@
-import { useMemo, useState } from "react";
-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 json from "../../../public/generated/js-docs/ListImperativeAPI.json";
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 { 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: "",
- value: ""
-};
-
-const ALIGNMENTS: Option[] = (
- ["auto", "center", "end", "smart", "start"] satisfies Align[]
-).map((value) => ({
- label: `align: ${value}`,
- value
-}));
-ALIGNMENTS.unshift(EMPTY_OPTION as Option);
-
-const BEHAVIORS: Option[] = (
- ["auto", "instant", "smooth"] satisfies ScrollBehavior[]
-).map((value) => ({
- label: `behavior: ${value}`,
- value
-}));
-BEHAVIORS.unshift(EMPTY_OPTION as Option);
+import { ImperativeHandle } from "../../components/handles/ImperativeHandle";
+import type { ImperativeHandleMetadata } from "../../types";
+import { html as usePanelRefHTML } from "../../../public/generated/code-snippets/useListRef.json";
+import { html as usePanelCallbackRefHTML } from "../../../public/generated/code-snippets/useListCallbackRef.json";
+import { ExternalLink } from "../../components/ExternalLink";
export default function ListImperativeApiRoute() {
- const [align, setAlign] = useState | undefined>();
- const [behavior, setBehavior] = useState<
- Option | undefined
- >();
- const [state, setState] = useState>(EMPTY_OPTION);
-
- const citiesByState = useCitiesByState();
-
- const stateOptions = useMemo []>(() => {
- const options: Option[] = citiesByState
- .filter((item) => item.type === "state")
- .map((item) => ({
- label: item.state,
- value: item.state
- }));
- options.unshift(EMPTY_OPTION);
-
- return options;
- }, [citiesByState]);
-
- const listRef = useListRef(null);
-
- const scrollToRow = () => {
- const index = citiesByState.findIndex(
- (item) => item.type === "state" && item.state === state.value
- );
- listRef.current?.scrollToRow({
- align: align?.value,
- behavior: behavior?.value,
- index
- });
- };
-
return (
-
+
+ Hooks
+
+ The usePanelRef hook returns a{" "}
+
+ mutable ref object
+
+ .
+
+
- List provides an imperative API for responding to events. The
- recommended way to access this API is to use the exported ref hook:
+ And the usePanelCallbackRef hook returns a{" "}
+
+ ref callback function
+
+ . This is better when sharing the ref with another hook or component.
-
- Attach the ref during render:
-
- And call API methods in an event handler:
-
- The form below uses the imperative API to scroll the list:
-
-
-
-
-
-
-
- Scroll
-
-
-
- {!citiesByState.length && }
-
-
-
- Note If you are passing the
- ref to another component or hook, use the ref callback function instead.
-
-
-
+
);
}
diff --git a/src/routes/list/PropsRoute.tsx b/src/routes/list/PropsRoute.tsx
index 273bb632..bac82a3e 100644
--- a/src/routes/list/PropsRoute.tsx
+++ b/src/routes/list/PropsRoute.tsx
@@ -1,11 +1,12 @@
import json from "../../../public/generated/js-docs/List.json";
import { Box } from "../../components/Box";
import { ComponentProps } from "../../components/props/ComponentProps";
+import type { ComponentMetadata } from "../../types";
export default function ListPropsRoute() {
return (
-
+
);
}
diff --git a/src/routes/list/ScrollToRowRoute.tsx b/src/routes/list/ScrollToRowRoute.tsx
new file mode 100644
index 00000000..9b157ce7
--- /dev/null
+++ b/src/routes/list/ScrollToRowRoute.tsx
@@ -0,0 +1,138 @@
+import { useMemo, useState } from "react";
+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 { 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: "",
+ value: ""
+};
+
+const ALIGNMENTS: Option[] = (
+ ["auto", "center", "end", "smart", "start"] satisfies Align[]
+).map((value) => ({
+ label: `align: ${value}`,
+ value
+}));
+ALIGNMENTS.unshift(EMPTY_OPTION as Option);
+
+const BEHAVIORS: Option[] = (
+ ["auto", "instant", "smooth"] satisfies ScrollBehavior[]
+).map((value) => ({
+ label: `behavior: ${value}`,
+ value
+}));
+BEHAVIORS.unshift(EMPTY_OPTION as Option);
+
+export default function ScrollToRowRoute() {
+ const [align, setAlign] = useState | undefined>();
+ const [behavior, setBehavior] = useState<
+ Option | undefined
+ >();
+ const [state, setState] = useState>(EMPTY_OPTION);
+
+ const citiesByState = useCitiesByState();
+
+ const stateOptions = useMemo []>(() => {
+ const options: Option[] = citiesByState
+ .filter((item) => item.type === "state")
+ .map((item) => ({
+ label: item.state,
+ value: item.state
+ }));
+ options.unshift(EMPTY_OPTION);
+
+ return options;
+ }, [citiesByState]);
+
+ const listRef = useListRef(null);
+
+ const scrollToRow = () => {
+ const index = citiesByState.findIndex(
+ (item) => item.type === "state" && item.state === state.value
+ );
+ listRef.current?.scrollToRow({
+ align: align?.value,
+ behavior: behavior?.value,
+ index
+ });
+ };
+
+ return (
+
+
+
+ List provides an imperative API for responding to events. The
+ recommended way to access this API is to use the exported ref hook:
+
+
+ Attach the ref during render:
+
+ And call API methods in an event handler:
+
+ The form below uses the imperative API to scroll the list:
+
+
+
+
+
+
+
+ Scroll
+
+
+
+ {!citiesByState.length && }
+
+
+
+ Note If you are passing the
+ ref to another component or hook, use the ref callback function instead.
+
+
+
+
+ );
+}
diff --git a/src/types.ts b/src/types.ts
index 981ac4d3..c48ca814 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -1,18 +1,35 @@
export type Intent = "danger" | "none" | "primary" | "success" | "warning";
+export type Section = {
+ content: string;
+ intent?: Intent;
+};
+
export type ComponentPropMetadata = {
- description: string;
+ description: Section[];
html: string;
- infos: string[];
name: string;
required: boolean;
- warnings: string[];
};
export type ComponentMetadata = {
+ description: Section[];
filePath: string;
name: string;
props: {
[name: string]: ComponentPropMetadata;
};
};
+
+export type ImperativeHandleMethodMetadata = {
+ description: Section[];
+ html: string;
+ name: string;
+};
+
+export type ImperativeHandleMetadata = {
+ description: Section[];
+ filePath: string;
+ name: string;
+ methods: ImperativeHandleMethodMetadata[];
+};
diff --git a/tsconfig.json b/tsconfig.json
index b13c1305..eeade829 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -7,7 +7,6 @@
"module": "ESNext",
"skipLibCheck": true,
- /* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
@@ -15,7 +14,6 @@
"noEmit": true,
"jsx": "react-jsx",
- /* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
@@ -23,7 +21,6 @@
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true,
- /* Custom */
"paths": {
"react-window": ["./lib"]
},