From 60f57e9f62ecebfcffe4177707c5b6afeb4d7d1e Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Wed, 3 Dec 2025 18:49:55 -0500 Subject: [PATCH 1/4] Update docs compilation pipelin and rendering components --- README.md | 7 +- lib/components/grid/Grid.tsx | 3 + lib/components/grid/types.ts | 25 +- lib/components/list/List.tsx | 3 + lib/components/list/types.ts | 12 +- package.json | 1 + pnpm-lock.yaml | 82 +++++++ public/generated/js-docs/Grid.json | 224 +++++++++++------- .../generated/js-docs/GridImperativeAPI.json | 42 ++++ public/generated/js-docs/List.json | 185 +++++++++------ .../generated/js-docs/ListImperativeAPI.json | 24 ++ scripts/compile-code-snippets.ts | 4 +- scripts/compile-docs.ts | 158 +----------- .../{ => code-snippets}/trimExcludedText.ts | 0 scripts/utils/docs/compileComponent.ts | 110 +++++++++ scripts/utils/docs/compileComponents.ts | 60 +++++ scripts/utils/docs/compileImperativeHandle.ts | 48 ++++ .../utils/docs/compileImperativeHandles.ts | 45 ++++ scripts/utils/docs/formatDescriptionText.ts | 10 + scripts/utils/{ => docs}/getPropTypeText.ts | 2 +- .../utils/{ => docs}/insertPropsMarkdown.ts | 0 scripts/utils/docs/parseDescription.ts | 55 +++++ scripts/utils/{ => docs}/propsToTable.ts | 26 +- scripts/utils/formatDescriptionText.ts | 6 - scripts/utils/syntax-highlight.ts | 4 +- src/components/DocsSection.tsx | 34 +++ src/components/handles/ImperativeHandle.tsx | 38 +++ .../handles/ImperativeHandleMethod.tsx | 19 ++ src/components/props/ComponentProp.tsx | 19 ++ src/components/props/ComponentProps.tsx | 14 +- .../props/ComponentPropsSection.tsx | 51 +--- src/nav/Nav.tsx | 10 +- src/routes.ts | 10 +- src/routes/grid/ImperativeHandleRoute.tsx | 36 +++ src/routes/grid/PropsRoute.tsx | 3 +- ...tiveApiRoute.tsx => ScrollToCellRoute.tsx} | 2 +- src/routes/list/ImperativeApiRoute.tsx | 152 ++---------- src/routes/list/PropsRoute.tsx | 3 +- src/routes/list/ScrollToRowRoute.tsx | 138 +++++++++++ src/types.ts | 23 +- tsconfig.json | 3 - 41 files changed, 1146 insertions(+), 545 deletions(-) create mode 100644 public/generated/js-docs/GridImperativeAPI.json create mode 100644 public/generated/js-docs/ListImperativeAPI.json rename scripts/utils/{ => code-snippets}/trimExcludedText.ts (100%) create mode 100644 scripts/utils/docs/compileComponent.ts create mode 100644 scripts/utils/docs/compileComponents.ts create mode 100644 scripts/utils/docs/compileImperativeHandle.ts create mode 100644 scripts/utils/docs/compileImperativeHandles.ts create mode 100644 scripts/utils/docs/formatDescriptionText.ts rename scripts/utils/{ => docs}/getPropTypeText.ts (93%) rename scripts/utils/{ => docs}/insertPropsMarkdown.ts (100%) create mode 100644 scripts/utils/docs/parseDescription.ts rename scripts/utils/{ => docs}/propsToTable.ts (54%) delete mode 100644 scripts/utils/formatDescriptionText.ts create mode 100644 src/components/DocsSection.tsx create mode 100644 src/components/handles/ImperativeHandle.tsx create mode 100644 src/components/handles/ImperativeHandleMethod.tsx create mode 100644 src/components/props/ComponentProp.tsx create mode 100644 src/routes/grid/ImperativeHandleRoute.tsx rename src/routes/grid/{ImperativeApiRoute.tsx => ScrollToCellRoute.tsx} (99%) create mode 100644 src/routes/list/ScrollToRowRoute.tsx diff --git a/README.md b/README.md index a55e21e1..49932290 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.

@@ -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..a7b2c7a6 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). */ 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..271a12d3 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,7 @@ "@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/node": "^24.2.0", "@types/react": "^19.1.8", "@types/react-dom": "^19.1.6", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a0a80cc1..e4c99f55 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -58,6 +58,9 @@ 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/node": specifier: ^24.2.0 version: 24.2.0 @@ -1274,6 +1277,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 +1705,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: { @@ -2831,6 +2857,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: { @@ -3615,6 +3648,13 @@ packages: } engines: { node: ">=8" } + path-type@6.0.0: + resolution: + { + integrity: sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ== + } + engines: { node: ">=18" } + pathe@2.0.3: resolution: { @@ -4041,6 +4081,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: { @@ -4419,6 +4466,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 +5502,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 +5747,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": {} @@ -6371,6 +6438,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: {} @@ -6777,6 +6853,8 @@ snapshots: path-type@4.0.0: {} + path-type@6.0.0: {} + pathe@2.0.3: {} pathval@2.0.1: {} @@ -6976,6 +7054,8 @@ snapshots: signal-exit@4.1.0: {} + slash@5.1.0: {} + slice-ansi@5.0.0: dependencies: ansi-styles: 6.2.1 @@ -7203,6 +7283,8 @@ snapshots: undici-types@7.10.0: {} + unicorn-magic@0.3.0: {} + universalify@2.0.1: {} update-browserslist-db@1.1.3(browserslist@4.25.1): diff --git a/public/generated/js-docs/Grid.json b/public/generated/js-docs/Grid.json index a98d07cc..4fced1db 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." + } + ], "filePath": "lib/components/grid/Grid.tsx", "name": "Grid", "props": { "className": { - "description": "CSS class name.", - "html": "
className?: string | undefined
", - "infos": [], + "description": [ + { + "content": "CSS class name." + } + ], + "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." + }, + { + "content": "See HTML dir [global attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/dir).", + "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." + } + ], + "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." + } + ], "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." + }, + { + "content": "This component will receive an index and style prop by default.\nAdditionally it will receive prop values passed to cellProps." + }, + { + "content": "The prop types for this component are exported as CellComponentProps", + "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." + }, + { + "content": "This object must not contain ariaAttributes, columnIndex, rowIndex, or style props.", + "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." + } + ], "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:
• 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" + } + ], "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." + } + ], + "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." + } + ], + "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." + }, + { + "content": "The useGridRef and useGridCallbackRef hooks are exported for convenience use in TypeScript projects.", + "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." + } + ], + "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." + } + ], + "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." + } + ], + "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." + } + ], "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:
• 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" + } + ], "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." + }, + { + "content": "In most use cases the default ARIA roles are sufficient and this prop is not needed.", + "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..f20d430b --- /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." + }, + { + "content": "The useGridRef and useGridCallbackRef hooks are exported for convenience use in TypeScript projects.", + "intent": "primary" + } + ], + "filePath": "lib/components/grid/types.ts", + "methods": [ + { + "description": [ + { + "content": "Scrolls the grid so that the specified row and column are visible." + } + ], + "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." + } + ], + "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." + } + ], + "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..ef6a50df 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." + } + ], "filePath": "lib/components/list/List.tsx", "name": "List", "props": { "className": { - "description": "CSS class name.", - "html": "
className?: string | undefined
", - "infos": [], + "description": [ + { + "content": "CSS class name." + } + ], + "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." + } + ], + "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." + } + ], "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." + } + ], + "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." + }, + { + "content": "This API has imperative methods for scrolling and a getter for the outermost DOM element." + }, + { + "content": "The useListRef and useListCallbackRef hooks are exported for convenience use in TypeScript projects.", + "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." + } + ], + "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." + } + ], + "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." + } + ], + "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." + }, + { + "content": "This component will receive an index and style prop by default.\nAdditionally it will receive prop values passed to rowProps." + }, + { + "content": "The prop types for this component are exported as RowComponentProps", + "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." + } + ], "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:
• 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" + }, + { + "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.", + "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." + }, + { + "content": "This object must not contain ariaAttributes, index, or style props.", + "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." + }, + { + "content": "In most use cases the default ARIA roles are sufficient and this prop is not needed.", + "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..fc942657 --- /dev/null +++ b/public/generated/js-docs/ListImperativeAPI.json @@ -0,0 +1,24 @@ +{ + "description": [ + { + "content": "Imperative List API." + }, + { + "content": "The useListRef and useListCallbackRef hooks are exported for convenience use in TypeScript projects.", + "intent": "primary" + } + ], + "filePath": "lib/components/list/types.ts", + "methods": [ + { + "description": [ + { + "content": "Scrolls the list so that the specified row is visible." + } + ], + "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..3bc8fd6e --- /dev/null +++ b/scripts/utils/docs/formatDescriptionText.ts @@ -0,0 +1,10 @@ +export function formatDescriptionText(text: string) { + return text + .replaceAll("\n- ", "
• ") + .replaceAll("\n\n", "

") + .replaceAll(/~~([^~]+)~~/g, "$1") + .replaceAll(/\*\*([^*]+)\*\*/g, "$1") + .replaceAll(/\*([^*]+)\*/g, "$1") + .replaceAll(/_([^_]+)_/g, "$1") + .replaceAll(/`([^`]+)`/g, "$1"); +} 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..a149e58f --- /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.substring(char.length + 1); + } + } + } + + 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..0c8ec797 --- /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/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/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:
- - - - - + + + + + {!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"] }, From 639b80bfd5e58e6556ec703c4bef57fb1f42bc89 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Wed, 3 Dec 2025 19:18:38 -0500 Subject: [PATCH 2/4] Replaced custom markdown processor for better formatting --- README.md | 2 +- lib/components/grid/types.ts | 2 +- package.json | 2 + pnpm-lock.yaml | 109 +++++++++++++++++- public/generated/js-docs/Grid.json | 48 ++++---- .../generated/js-docs/GridImperativeAPI.json | 10 +- public/generated/js-docs/List.json | 42 +++---- .../generated/js-docs/ListImperativeAPI.json | 6 +- scripts/utils/docs/formatDescriptionText.ts | 17 +-- scripts/utils/docs/parseDescription.ts | 4 +- 10 files changed, 175 insertions(+), 67 deletions(-) diff --git a/README.md b/README.md index 49932290..ddb5a59f 100644 --- a/README.md +++ b/README.md @@ -239,7 +239,7 @@ Grid will automatically re-render cells when values in this object change.

dir

Indicates the directionality of grid cells.

-

ℹ️ See HTML dir global attribute.

+

ℹ️ See HTML dir global attribute for more information.

diff --git a/lib/components/grid/types.ts b/lib/components/grid/types.ts index a7b2c7a6..a794318a 100644 --- a/lib/components/grid/types.ts +++ b/lib/components/grid/types.ts @@ -87,7 +87,7 @@ export type GridProps< /** * Indicates the directionality of grid cells. * - * ℹ️ See HTML `dir` [global attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/dir). + * ℹ️ See HTML `dir` [global attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/dir) for more information. */ dir?: "ltr" | "rtl"; diff --git a/package.json b/package.json index 271a12d3..4a243816 100644 --- a/package.json +++ b/package.json @@ -62,6 +62,7 @@ "@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", @@ -75,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 e4c99f55..9f9fd9ce 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -61,6 +61,9 @@ importers: "@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 @@ -100,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 @@ -165,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)) @@ -1763,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: { @@ -1781,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: { @@ -3302,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: { @@ -3400,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: { @@ -3408,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: { @@ -3801,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: { @@ -4448,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: { @@ -5774,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 @@ -6662,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 @@ -6732,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: {} @@ -6901,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: {} @@ -7277,6 +7379,8 @@ snapshots: typescript@5.8.3: {} + uc.micro@2.1.0: {} + ufo@1.6.1: {} undici-types@5.26.5: {} @@ -7370,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 @@ -7396,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 4fced1db..f170bdce 100644 --- a/public/generated/js-docs/Grid.json +++ b/public/generated/js-docs/Grid.json @@ -1,7 +1,7 @@ { "description": [ { - "content": "Renders data with many rows and columns." + "content": "

Renders data with many rows and columns.

\n" } ], "filePath": "lib/components/grid/Grid.tsx", @@ -10,7 +10,7 @@ "className": { "description": [ { - "content": "CSS class name." + "content": "

CSS class name.

\n" } ], "html": "
className?: string
", @@ -20,10 +20,10 @@ "dir": { "description": [ { - "content": "Indicates the directionality of grid cells." + "content": "

Indicates the directionality of grid cells.

\n" }, { - "content": "See HTML dir [global attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/dir).", + "content": "

See HTML dir global attribute for more information.

\n", "intent": "primary" } ], @@ -34,7 +34,7 @@ "style": { "description": [ { - "content": "Optional CSS properties.\nThe grid of cells will fill the height and width defined by this style." + "content": "

Optional CSS properties.\nThe grid of cells will fill the height and width defined by this style.

\n" } ], "html": "
style?: CSSProperties
", @@ -44,7 +44,7 @@ "children": { "description": [ { - "content": "Additional content to be rendered within the grid (above cells).\nThis property can be used to render things like overlays or tooltips." + "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
", @@ -54,13 +54,13 @@ "cellComponent": { "description": [ { - "content": "React component responsible for rendering a cell." + "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." + "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", + "content": "

The prop types for this component are exported as CellComponentProps

\n", "intent": "primary" } ], @@ -71,10 +71,10 @@ "cellProps": { "description": [ { - "content": "Additional props to be passed to the cell-rendering component.\nGrid will automatically re-render cells when values in this object change." + "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.", + "content": "

This object must not contain ariaAttributes, columnIndex, rowIndex, or style props.

\n", "intent": "warning" } ], @@ -85,7 +85,7 @@ "columnCount": { "description": [ { - "content": "Number of columns to be rendered in the grid." + "content": "

Number of columns to be rendered in the grid.

\n" } ], "html": "
columnCount: number
", @@ -95,7 +95,7 @@ "columnWidth": { "description": [ { - "content": "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" + "content": "

Column width; the following formats are supported:

\n
    \n
  • number of pixels (number)
  • \n
  • percentage of the grid's current width (string)
  • \n
  • function that returns the row width (in pixels) given an index and cellProps
  • \n
\n" } ], "html": "
columnWidth: string | number | ((index: number, cellProps: CellProps) => number)
", @@ -105,7 +105,7 @@ "defaultHeight": { "description": [ { - "content": "Default height of grid for initial render.\nThis value is important for server rendering." + "content": "

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

\n" } ], "html": "
defaultHeight?: number = 0
", @@ -115,7 +115,7 @@ "defaultWidth": { "description": [ { - "content": "Default width of grid for initial render.\nThis value is important for server rendering." + "content": "

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

\n" } ], "html": "
defaultWidth?: number = 0
", @@ -125,10 +125,10 @@ "gridRef": { "description": [ { - "content": "Imperative Grid API." + "content": "

Imperative Grid API.

\n" }, { - "content": "The useGridRef and useGridCallbackRef hooks are exported for convenience use in TypeScript projects.", + "content": "

The useGridRef and useGridCallbackRef hooks are exported for convenience use in TypeScript projects.

\n", "intent": "primary" } ], @@ -139,7 +139,7 @@ "onCellsRendered": { "description": [ { - "content": "Callback notified when the range of rendered cells changes." + "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)
", @@ -149,7 +149,7 @@ "onResize": { "description": [ { - "content": "Callback notified when the Grid's outermost HTMLElement resizes.\nThis may be used to (re)scroll a cell into view." + "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)
", @@ -159,7 +159,7 @@ "overscanCount": { "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." + "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
", @@ -169,7 +169,7 @@ "rowCount": { "description": [ { - "content": "Number of rows to be rendered in the grid." + "content": "

Number of rows to be rendered in the grid.

\n" } ], "html": "
rowCount: number
", @@ -179,7 +179,7 @@ "rowHeight": { "description": [ { - "content": "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" + "content": "

Row height; the following formats are supported:

\n
    \n
  • number of pixels (number)
  • \n
  • percentage of the grid's current height (string)
  • \n
  • function that returns the row height (in pixels) given an index and cellProps
  • \n
\n" } ], "html": "
rowHeight: string | number | ((index: number, cellProps: CellProps) => number)
", @@ -189,10 +189,10 @@ "tagName": { "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." + "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.", + "content": "

In most use cases the default ARIA roles are sufficient and this prop is not needed.

\n", "intent": "warning" } ], diff --git a/public/generated/js-docs/GridImperativeAPI.json b/public/generated/js-docs/GridImperativeAPI.json index f20d430b..a199d027 100644 --- a/public/generated/js-docs/GridImperativeAPI.json +++ b/public/generated/js-docs/GridImperativeAPI.json @@ -1,10 +1,10 @@ { "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." + "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.", + "content": "

The useGridRef and useGridCallbackRef hooks are exported for convenience use in TypeScript projects.

\n", "intent": "primary" } ], @@ -13,7 +13,7 @@ { "description": [ { - "content": "Scrolls the grid so that the specified row and column are visible." + "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;
", @@ -22,7 +22,7 @@ { "description": [ { - "content": "Scrolls the grid so that the specified column is visible." + "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;
", @@ -31,7 +31,7 @@ { "description": [ { - "content": "Scrolls the grid so that the specified row is visible." + "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;
", diff --git a/public/generated/js-docs/List.json b/public/generated/js-docs/List.json index ef6a50df..e2a99107 100644 --- a/public/generated/js-docs/List.json +++ b/public/generated/js-docs/List.json @@ -1,7 +1,7 @@ { "description": [ { - "content": "Renders data with many rows." + "content": "

Renders data with many rows.

\n" } ], "filePath": "lib/components/list/List.tsx", @@ -10,7 +10,7 @@ "className": { "description": [ { - "content": "CSS class name." + "content": "

CSS class name.

\n" } ], "html": "
className?: string
", @@ -20,7 +20,7 @@ "style": { "description": [ { - "content": "Optional CSS properties.\nThe list of rows will fill the height defined by this style." + "content": "

Optional CSS properties.\nThe list of rows will fill the height defined by this style.

\n" } ], "html": "
style?: CSSProperties
", @@ -30,7 +30,7 @@ "children": { "description": [ { - "content": "Additional content to be rendered within the list (above cells).\nThis property can be used to render things like overlays or tooltips." + "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
", @@ -40,7 +40,7 @@ "defaultHeight": { "description": [ { - "content": "Default height of list for initial render.\nThis value is important for server rendering." + "content": "

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

\n" } ], "html": "
defaultHeight?: number = 0
", @@ -50,13 +50,13 @@ "listRef": { "description": [ { - "content": "Ref used to interact with this component's imperative API." + "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." + "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.", + "content": "

The useListRef and useListCallbackRef hooks are exported for convenience use in TypeScript projects.

\n", "intent": "primary" } ], @@ -67,7 +67,7 @@ "onResize": { "description": [ { - "content": "Callback notified when the List's outermost HTMLElement resizes.\nThis may be used to (re)scroll a row into view." + "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)
", @@ -77,7 +77,7 @@ "onRowsRendered": { "description": [ { - "content": "Callback notified when the range of visible rows changes." + "content": "

Callback notified when the range of visible rows changes.

\n" } ], "html": "
onRowsRendered?: ((visibleRows: { startIndex: number; stopIndex: number; }, allRows: { startIndex: number; stopIndex: number; }) => void)
", @@ -87,7 +87,7 @@ "overscanCount": { "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." + "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
", @@ -97,13 +97,13 @@ "rowComponent": { "description": [ { - "content": "React component responsible for rendering a row." + "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." + "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", + "content": "

The prop types for this component are exported as RowComponentProps

\n", "intent": "primary" } ], @@ -114,7 +114,7 @@ "rowCount": { "description": [ { - "content": "Number of items to be rendered in the list." + "content": "

Number of items to be rendered in the list.

\n" } ], "html": "
rowCount: number
", @@ -124,10 +124,10 @@ "rowHeight": { "description": [ { - "content": "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" + "content": "

Row height; the following formats are supported:

\n
    \n
  • number of pixels (number)
  • \n
  • percentage of the grid's current height (string)
  • \n
  • function that returns the row height (in pixels) given an index and cellProps
  • \n
  • dynamic 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.", + "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" } ], @@ -138,10 +138,10 @@ "rowProps": { "description": [ { - "content": "Additional props to be passed to the row-rendering component.\nList will automatically re-render rows when values in this object change." + "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.", + "content": "

This object must not contain ariaAttributes, index, or style props.

\n", "intent": "warning" } ], @@ -152,10 +152,10 @@ "tagName": { "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." + "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.", + "content": "

In most use cases the default ARIA roles are sufficient and this prop is not needed.

\n", "intent": "warning" } ], diff --git a/public/generated/js-docs/ListImperativeAPI.json b/public/generated/js-docs/ListImperativeAPI.json index fc942657..8be088ee 100644 --- a/public/generated/js-docs/ListImperativeAPI.json +++ b/public/generated/js-docs/ListImperativeAPI.json @@ -1,10 +1,10 @@ { "description": [ { - "content": "Imperative List API." + "content": "

Imperative List API.

\n" }, { - "content": "The useListRef and useListCallbackRef hooks are exported for convenience use in TypeScript projects.", + "content": "

The useListRef and useListCallbackRef hooks are exported for convenience use in TypeScript projects.

\n", "intent": "primary" } ], @@ -13,7 +13,7 @@ { "description": [ { - "content": "Scrolls the list so that the specified row is visible." + "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;
", diff --git a/scripts/utils/docs/formatDescriptionText.ts b/scripts/utils/docs/formatDescriptionText.ts index 3bc8fd6e..9e047cf1 100644 --- a/scripts/utils/docs/formatDescriptionText.ts +++ b/scripts/utils/docs/formatDescriptionText.ts @@ -1,10 +1,11 @@ +import Markdown from "markdown-it"; + +let processor: Markdown | undefined = undefined; + export function formatDescriptionText(text: string) { - return text - .replaceAll("\n- ", "
• ") - .replaceAll("\n\n", "

") - .replaceAll(/~~([^~]+)~~/g, "$1") - .replaceAll(/\*\*([^*]+)\*\*/g, "$1") - .replaceAll(/\*([^*]+)\*/g, "$1") - .replaceAll(/_([^_]+)_/g, "$1") - .replaceAll(/`([^`]+)`/g, "$1"); + if (processor === undefined) { + processor = new Markdown(); + } + + return processor.render(text); } diff --git a/scripts/utils/docs/parseDescription.ts b/scripts/utils/docs/parseDescription.ts index a149e58f..daaad5e4 100644 --- a/scripts/utils/docs/parseDescription.ts +++ b/scripts/utils/docs/parseDescription.ts @@ -30,9 +30,9 @@ export async function parseDescription(rawText: string) { content = formatDescriptionText(chunk.trim()); for (const char in INTENT_FLAGS) { - if (content.startsWith(char)) { + if (content.startsWith(`

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

${char} `, "

"); } } } From ea2cad9eaa5d0ce0c25c0034e8579ee072e03e32 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Wed, 3 Dec 2025 19:25:58 -0500 Subject: [PATCH 3/4] Fix continue links --- src/routes/grid/RenderingGridRoute.tsx | 2 +- src/routes/list/DynamicRowHeightsRoute.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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/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. - + ); } From d0b98a3e678fd6bece99bb21c1a4a53faecb788e Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Wed, 3 Dec 2025 19:39:49 -0500 Subject: [PATCH 4/4] Fix

    formatting --- src/components/DocsSection.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/DocsSection.tsx b/src/components/DocsSection.tsx index 0c8ec797..c6e3ca6d 100644 --- a/src/components/DocsSection.tsx +++ b/src/components/DocsSection.tsx @@ -24,7 +24,7 @@ export function DocsSection({
    ", '
      ') }} >
    );