import { CellRenderContext, ColumnEditorContext, ColumnEditorSpec, ColumnViewRenderer, GridConfig as CoreGridConfig, FrameworkAdapter, TypeDefault } from '@toolbox-web/grid'; import { ReactNode } from 'react'; import { registerChildFeatureDetector, ChildFeatureDetector } from './child-feature-detector'; import { registerEditorMountHook, EditorMountHook } from './editor-mount-hooks'; import { registerFeaturePropKey } from './feature-prop-keys'; import { TypeDefaultsMap } from './grid-type-registry'; import { registerPostMountRefresh, PostMountRefreshHook } from './post-mount-refresh-hooks'; export { registerEditorMountHook, type EditorMountHook }; export { registerPostMountRefresh, type PostMountRefreshHook }; export { registerChildFeatureDetector, type ChildFeatureDetector }; export { registerFeaturePropKey }; /** * Context handed to feature bridge installers so they can hook the adapter's * portal lifecycle without depending on its private fields. * @internal */ export interface FeatureBridgeContext { /** Track a portal key + container for cleanup on releaseCell / destroy. */ trackPortal(key: string, container: HTMLElement, isEditor: boolean): void; } /** * Installer signature: given a grid element + bridge context, returns the * row-renderer the adapter should expose, or undefined if no React template * is registered for that grid. * @internal */ export type RowRendererBridge = (gridEl: HTMLElement, ctx: FeatureBridgeContext) => ((row: TRow, rowIndex: number) => HTMLElement) | undefined; /** * Installer signature for the type-default `filterPanelRenderer` wrapper. * Receives the user's React render function (typed loosely as `unknown` so * the adapter does not depend on filtering types) and returns the imperative * `(container, params) => void` form required by the core grid. * @internal */ export type FilterPanelTypeDefaultBridge = (renderFn: unknown, gridEl: HTMLElement | undefined, ctx: FeatureBridgeContext) => NonNullable; /** * Install the master-detail row-renderer bridge. Called once on import by * `@toolbox-web/grid-react/features/master-detail`. Mirrors how core grid * plugins augment the grid via `registerPlugin()`. * @internal Plugin API */ export declare function registerDetailRendererBridge(bridge: RowRendererBridge): void; /** * Install the responsive card row-renderer bridge. Called once on import by * `@toolbox-web/grid-react/features/responsive`. * @internal Plugin API */ export declare function registerResponsiveCardRendererBridge(bridge: RowRendererBridge): void; /** * Install the type-default `filterPanelRenderer` wrapper. Called once on * import by `@toolbox-web/grid-react/features/filtering`. Without this * bridge, type-default filterPanelRenderer entries are dropped silently — * filter panels only work if the filtering feature is also imported, which * is the same precondition as the FilteringPlugin itself (TBW031). * @internal Plugin API */ export declare function registerFilterPanelTypeDefaultBridge(bridge: FilterPanelTypeDefaultBridge): void; /** * Register a React cell renderer for a column element. * Called by GridColumn when it has a children render prop. */ export declare function registerColumnRenderer(element: HTMLElement, renderer: (ctx: CellRenderContext) => ReactNode): void; /** * Register a React cell editor for a column element. * Called by GridColumn when it has an editor prop. */ export declare function registerColumnEditor(element: HTMLElement, editor: (ctx: ColumnEditorContext) => ReactNode): void; /** * Get the renderer registered for a column element. * Falls back to field-based lookup if WeakMap lookup fails. */ export declare function getColumnRenderer(element: HTMLElement): ((ctx: CellRenderContext) => ReactNode) | undefined; /** * Get the editor registered for a column element. * Falls back to field-based lookup if WeakMap lookup fails. */ export declare function getColumnEditor(element: HTMLElement): ((ctx: ColumnEditorContext) => ReactNode) | undefined; export declare function registerTypeRenderer(element: HTMLElement, renderer: (ctx: CellRenderContext) => ReactNode): void; export declare function getTypeRenderer(element: HTMLElement): ((ctx: CellRenderContext) => ReactNode) | undefined; export declare function registerTypeEditor(element: HTMLElement, editor: (ctx: ColumnEditorContext) => ReactNode): void; export declare function getTypeEditor(element: HTMLElement): ((ctx: ColumnEditorContext) => ReactNode) | undefined; /** * Debug helper: Get list of registered fields. * @internal */ export declare function getRegisteredFields(): string[]; /** @internal */ export declare function getRegisteredTypes(): string[]; /** * Framework adapter that enables React component integration * with the grid's light DOM configuration API. * * ## Usage * * The adapter is automatically registered when using the DataGrid component. * For advanced use cases, you can manually register: * * ```tsx * import { GridElement } from '@toolbox-web/grid'; * import { GridAdapter } from '@toolbox-web/grid-react'; * * // One-time registration * GridElement.registerAdapter(new GridAdapter()); * ``` * * ## Declarative usage with DataGrid: * * ```tsx * * * {(ctx) => } * * ( * * )} /> * * ``` * @since 0.11.0 */ export declare class GridAdapter implements FrameworkAdapter { /** Portal keys for all managed portals (for cleanup on destroy). */ private allPortalKeys; /** Portal keys for editor-specific portals (for per-cell cleanup via releaseCell). */ private editorPortalKeys; /** Maps portal keys to their containers (for container-based lookups in releaseCell/unmount). */ private keyToContainer; /** * Reverse index: container element → portal key. Maintained alongside * `keyToContainer` so `releaseCell` can resolve only the containers * actually inside the released cell (via `cellEl.querySelectorAll`) * instead of scanning every tracked portal — important for grids with * many cells, where `releaseCell` runs on every cell teardown. */ private containerToKey; /** * Per-editor `before-edit-close` listener teardown functions, keyed by portal key. * * The grid's editing plugin emits `before-edit-close` on the host `` * before tearing down a row's managed editors. React editors commonly write * `onBlur={commit}` to flush local state on click-away, but Tab / programmatic * row exit rebuilds the cell DOM synchronously without giving the focused * input a chance to fire `blur` first. * * To bridge that gap we call `.blur()` on the focused input inside the editor * container as soon as `before-edit-close` fires. The native `.blur()` method * is what React's event system actually observes — React 17+ delegates focus * events at the root by listening to `focusout` (bubbling) and mapping it to * `onBlur`. A bare `dispatchEvent(new FocusEvent('blur'))` would be ignored * by React because `blur` does not bubble. Calling the native `.blur()` * dispatches the full focus-loss chain (`blur` + `focusout`) and also moves * focus off the input — both desirable here since the editor DOM is about to * be torn down anyway. * * Mirrors the Angular adapter's `BaseGridEditor.onBeforeEditClose()` hook. * Requires zero per-editor code: any editor with `onBlur={commit}` is * automatically covered for Tab commits and programmatic row exit. */ private editorBeforeCloseUnsubs; private typeDefaults; /** * Sets the type defaults map for this adapter. * Called by DataGrid when it receives type defaults from context. * * @internal */ setTypeDefaults(defaults: TypeDefaultsMap | null): void; /** * FrameworkAdapter.processConfig implementation. * Called automatically by the grid's `set gridConfig` setter. * Converts React renderer/editor functions to DOM-returning functions. */ processConfig(config: CoreGridConfig): CoreGridConfig; /** * Determines if this adapter can handle the given element. * Checks if a renderer or editor is registered for this element. */ canHandle(element: HTMLElement): boolean; /** * Creates a view renderer function that renders a React component * and returns its container DOM element. * * Uses a cell cache to reuse portals for performance - instead of * creating new portals on each render, we reuse existing ones and * update their content. * * Returns undefined if no renderer is registered for this element, * allowing the grid to use its default rendering. */ createRenderer(element: HTMLElement): ColumnViewRenderer | undefined; /** * Creates an editor spec that renders a React component * with commit/cancel callbacks passed as props. */ createEditor(element: HTMLElement): ColumnEditorSpec; /** * Creates a detail renderer function for MasterDetailPlugin. * Implementation is installed by `@toolbox-web/grid-react/features/master-detail` * via `registerDetailRendererBridge`. Returns undefined if the * master-detail feature has not been imported, or if no GridDetailPanel * was registered for this grid. */ createDetailRenderer(gridElement: HTMLElement): ((row: TRow, rowIndex: number) => HTMLElement) | undefined; /** * Framework adapter hook called by MasterDetailPlugin during attach(). * Parses the element and returns a React-based renderer. */ parseDetailElement(detailElement: Element): ((row: TRow, rowIndex: number) => HTMLElement | string) | undefined; /** * Creates a responsive card renderer function for ResponsivePlugin. * Implementation is installed by `@toolbox-web/grid-react/features/responsive` * via `registerResponsiveCardRendererBridge`. Returns undefined if * the responsive feature has not been imported, or if no GridResponsiveCard * was registered for this grid. */ createResponsiveCardRenderer(gridElement: HTMLElement): ((row: TRow, rowIndex: number) => HTMLElement) | undefined; /** * FrameworkAdapter hook called by ResponsivePlugin during attach(). * Parses the `` element and delegates to * {@link createResponsiveCardRenderer}. Needed for parity with the Vue * and Angular adapters so ResponsivePlugin's standard lookup path works * for React users as well, not just via the imperative * `refreshResponsiveCardRenderer` hook in DataGrid. */ parseResponsiveCardElement(cardElement: Element): ((row: TRow, rowIndex: number) => HTMLElement) | undefined; /** * Creates a tool panel renderer from a light DOM element. * Renders React components into tool panel containers. */ createToolPanelRenderer(element: HTMLElement): ((container: HTMLElement) => void | (() => void)) | undefined; /** * Gets type-level defaults from the type defaults map. * * This enables application-wide type defaults configured via GridTypeProvider. * The returned TypeDefault contains renderer/editor functions that render * React components into the grid's cells. * * @example * ```tsx * // App.tsx * const typeDefaults = { * country: { * renderer: (ctx) => , * editor: (ctx) => * } * }; * * * * * * // Any grid with type: 'country' columns will use these components * ``` */ getTypeDefault(type: string, gridEl?: HTMLElement): TypeDefault | undefined; /** * Creates a renderer function from a React render function for type defaults. * @internal */ private createTypeRenderer; /** * Creates an editor function from a React render function for type defaults. * @internal */ private createTypeEditor; /** * Stable bridge context handed to feature installers. Bound once per adapter * so feature bridges can call `trackPortal` without each invocation creating * a fresh closure. */ private readonly bridgeContext; /** Register a portal key for lifecycle tracking. */ private trackPortal; /** Unregister a portal key from all tracking sets. */ private untrackPortal; /** * Clean up all mounted portals. * Call this when the grid is unmounted. */ destroy(): void; /** * Called when a cell's content is about to be wiped. * * Destroys editor portals AND renderer portals whose container is inside * the cell. Releasing renderers here is critical: the editing pipeline * runs `cell.innerHTML = ''` immediately after this returns, which * silently detaches React-managed renderer containers from the DOM. * If we leave the React root mounted, its fiber tree still believes * the now-orphaned children are present — a subsequent React commit * (e.g. the user's `onCellCommit` → `setRows`) tries to `removeChild` * those gone nodes and throws `NotFoundError` (issue #250). * * Calling this BEFORE the wipe lets React unmount cleanly while DOM * still matches its fiber tree. The `wrapReactRenderer` cache will * detect the missing entry on the next render and create a fresh * container — see the cache-validation defense in `createRenderer` * and `wrapReactRenderer` (`react-column-config.ts`). * * Also cleans up config-based editor and renderer roots * (from processGridConfig/wrapReactEditor/wrapReactRenderer) that * bypass the adapter's own portal tracking. */ releaseCell(cellEl: HTMLElement): void; /** * Unmount a specific container (called when cell is recycled). */ unmount(container: HTMLElement): void; /** * Open a teardown batch on the PortalManager. Grid core wraps multi-cell * release loops (`_clearRowPool`, row-pool shrink, full row rebuild) with * `beginBatch` / `endBatch` so the N per-cell sync removals collapse to a * single deferred render instead of N `flushSync` calls. Eliminates the * "flushSync was called from inside a lifecycle method" warning storm on * grouping changes (#330). */ beginBatch(gridEl?: HTMLElement): void; /** * Close a teardown batch opened by {@link beginBatch}. Caller is * responsible for ensuring affected containers are detached from the * DOM before this returns; the manager's render-time `isConnected` * filter then prunes them silently. */ endBatch(gridEl?: HTMLElement): void; } //# sourceMappingURL=react-grid-adapter.d.ts.map