/** * Decides which editor a cell opens with — the strategy chain that replaces the * `switch (colDef.type)` the old `cell-editor-engine.ts` used. * * ### Priority (hard requirement) * Six strategies run in this exact order, first non-`null` answer wins: * * 1. **`editable`** — is this cell editable at all? A locked column, a falsy * `editable`, or a predicate returning `false` short-circuits the whole chain * with `{ kind: 'none' }`. Nothing below it can re-open a cell the column * closed. * 2. **`explicit`** — `colDef.cellEditor` is a Photon editor class, a factory, * or an already-built {@link ICellEditor}. A hand-written editor is the * author's most specific instruction, so it outranks everything, and running * it before the adapters guarantees a plain JavaScript editor class is never * handed to a framework wrapper that happens to accept functions. * 3. **`adapter`** — a registered {@link FrameworkEditorAdapter} claims the * spec. Must come *before* the string lookup so an adapter can claim a * non-string spec (an Angular component, a React component, a Vue options * object) that no registry key would ever match. * 4. **`registered`** — `cellEditor` is a string naming an entry in the * {@link EditorRegistry}. An unknown key warns and *continues* the chain: a * typo must not make a column uneditable. * 5. **`byType`** — the editor inferred from `colDef.type` via * {@link DEFAULT_EDITOR_BY_TYPE}, which is the behaviour a column with no * `cellEditor` at all relies on. * 6. **`fallback`** — the `'text'` editor, so a column with an exotic type and a * slim registry still edits as text rather than silently refusing to open. * * The order is data, not control flow: {@link EditorResolver} walks an array, * and {@link EditorResolver.use} can splice a host strategy in at any index — * a "read-only while the row is syncing" rule, say — without this file changing. * * @packageDocumentation */ import type { ColumnDataType, ColumnDef } from '../../types/column.types'; import type { RowNode } from '../../types/row.types'; import type { BuiltInEditorName, ICellEditor } from '../types/cell-editor.types'; import type { EditorAdapterRegistry } from './editor-adapter-registry'; import type { EditorRegistry } from './editor-registry'; /** * The editor a column gets from its `type` when it names none. * * A data map rather than a `switch`: it is enumerable (docs and the column * settings panel read it), overridable key by key, and adding a column type * costs one line here instead of a new branch in the engine. * * Several types share an editor on purpose — `currency`, `percentage` and * `duration` are all numbers behind their formatting, and `object` / `array` * are edited by picking from options, exactly as `dropdown` is. * * `sparkline` is deliberately absent: a mini-chart column has no scalar the user * could type. Such a column falls through to the `fallback` strategy and edits * as text only if the host explicitly marks it `editable`. */ export declare const DEFAULT_EDITOR_BY_TYPE: Readonly>>; /** * Everything a strategy is told about the cell being opened. * * One frozen bag rather than positional arguments, so a future strategy can ask * a new question without changing every existing `resolve` signature. `api` is * `unknown` for the same reason `CellEditorParams.api` is: the registry sits * below `GridApi` in the dependency order and must not import it. */ export interface EditorResolutionRequest { /** The column whose cell is being opened. */ readonly colDef: ColumnDef; /** The row node being edited — what a per-row `editable` predicate inspects. */ readonly node: RowNode; /** The row's data object. Read-only here; strategies must never write to it. */ readonly data: Readonly>; /** Zero-based index of the row within the currently displayed rows. */ readonly rowIndex: number; /** The live grid API, forwarded untyped to `editable` predicates. */ readonly api: unknown; } /** * The answer: either this cell does not open an editor, or here is how to build * one. * * A discriminated union rather than `ICellEditor | null` for two reasons: the * caller gets a `reason` it can log or surface instead of a silent no-op, and * `strategy` names which rule decided — which is the difference between a * two-minute and a two-hour debugging session when a column opens the "wrong" * editor. */ export type EditorResolution = { /** No editor: the cell is not editable, or nothing could build one. */ readonly kind: 'none'; /** Human-readable explanation, safe to log. Never shown to end users. */ readonly reason: string; } | { /** An editor is available. */ readonly kind: 'editor'; /** Which strategy decided — `'explicit'`, `'adapter'`, `'registered'`, … */ readonly strategy: string; /** * Builds the editor for one session. * * Constructs a **fresh** instance per call for class, factory, registry * and adapter specs, so an editor may keep session state in fields with no * reset logic. The single exception is a column that supplied an * already-built {@link ICellEditor} object, where this necessarily returns * that same instance — see the `explicit` strategy for why that is a * footgun in a multi-grid page. */ readonly create: () => ICellEditor; }; /** What every strategy may consult. Passed in rather than imported, so a test — or a second grid with its own registry — is a constructor argument away. */ export interface EditorResolverDeps { /** Named editors, built-in and application-registered. */ readonly registry: EditorRegistry; /** The framework seam. Empty in a plain-JavaScript embedding. */ readonly adapters: EditorAdapterRegistry; } /** * One rule in the chain. * * @remarks * Returning `null` means "I have no opinion, ask the next one" and is what makes * the chain composable; returning `{ kind: 'none' }` is an active veto that ends * it. Only the `editable` strategy vetoes today. */ export interface EditorResolutionStrategy { /** Diagnostic name, surfaced as {@link EditorResolution.strategy}. */ readonly name: string; /** * @returns The resolution this rule dictates, or `null` to defer to the next * strategy. Must be cheap and side-effect free — it runs once per edit * session, and every strategy above the matching one runs too. */ resolve(request: EditorResolutionRequest, deps: EditorResolverDeps): EditorResolution | null; } /** * Runs the strategies in order and reports the first opinion. * * Not memoised, by the same reasoning as `renderer-resolver.ts`: this runs once * per edit session — a human-speed event, orders of magnitude rarer than a * render — and caching against a `ColumnDef` would buy nothing while risking * stale answers, since those objects are mutated in place elsewhere (`locked` is * toggled straight from the column menu). */ export declare class EditorResolver { private readonly deps; private readonly chain; /** * @param deps - The registries every strategy consults. * @param strategies - The chain to run. Defaults to * {@link createDefaultStrategies}; pass your own to replace the priority * order wholesale, or use {@link use} to splice into the default one. */ constructor(deps: EditorResolverDeps, strategies?: readonly EditorResolutionStrategy[]); /** * Inserts a strategy into the chain. * * @param strategy - The rule to add. * @param index - Where to insert. Appended when omitted — which places it * *after* `fallback`, so it can only ever act as a last resort. Pass `0` for * a veto that must outrank even the `editable` check, or `1` for a rule that * respects editability but overrides every editor choice. */ use(strategy: EditorResolutionStrategy, index?: number): void; /** The chain, in the order it runs. Read-only: use {@link use} to change it. */ strategies(): readonly EditorResolutionStrategy[]; /** * Resolves the editor for one cell. * * @returns The first non-`null` answer, or a `none` explaining that no rule * applied — never `null`, so the caller has exactly two cases to handle. */ resolve(request: EditorResolutionRequest): EditorResolution; } /** * The default chain, in priority order — see this file's header for why the * order is what it is. A function rather than a module constant so each grid * gets its own array and can splice into it without affecting its neighbours. */ export declare function createDefaultStrategies(): EditorResolutionStrategy[]; //# sourceMappingURL=default-editor-resolver.d.ts.map