@adaptabletools/adaptable
Version:
Powerful AG Grid extension which provides advanced, cutting-edge functionality to meet all DataGrid requirements
366 lines (365 loc) • 15 kB
TypeScript
import * as React from 'react';
import { AdaptableButton } from './AdaptableButton';
import { BaseContext } from '../../types';
/**
* Data which appears in an AdapTable Form.
*
* The default shape is a loosely-typed `Record<string, any>`, but consumers
* who care about strong typing can declare their own shape and pass it as
* the second generic parameter to {@link AdaptableForm}.
*/
export type AdaptableFormData = Record<string, any>;
/**
* Defines a form which appears dynamically; used by Alerts, Export Custom
* Destinations and Dashboard Custom Toolbars.
*
* @typeParam T - The host's button/handler context (e.g.
* `CustomToolbarFormContext`).
* @typeParam TData - Optional shape of the form data. Defaults to the
* loosely-typed {@link AdaptableFormData}; declare your
* own shape (e.g. `{ name: string; age: number }`) for
* strongly-typed `formData` in `onSubmit` callbacks.
*/
export interface AdaptableForm<T extends BaseContext, TData extends AdaptableFormData = AdaptableFormData> {
/**
* Title to appear in the Form
*/
title?: string;
/**
* Additional information to appear in the Form
*/
description?: string;
/**
* Collection of Dynamic Fields and Field Groups to display.
*
* Items can be:
* - a single {@link AdaptableFormField} (rendered on its own row in the
* default `rows` layout, or as one cell in the `inline` layout),
* - an array of {@link AdaptableFormField} (rendered side-by-side on the
* same row in the `rows` layout), or
* - an {@link AdaptableFormFieldGroup} (a labelled section that
* visually groups related fields).
*/
fields?: (AdaptableFormField | AdaptableFormField[] | AdaptableFormFieldGroup)[];
/**
* Buttons to include in the Form
*/
buttons?: AdaptableButton<T>[];
/**
* How the form's fields are arranged on screen.
*
* - `rows` (default): each field on its own row, label on the left and
* input on the right. Suitable for popups / wizards.
* - `inline`: fields are placed side-by-side on a single horizontal line,
* with the label rendered immediately before its input. Designed for
* compact contexts such as Dashboard Custom Toolbars where vertical
* real-estate is scarce.
*
* @defaultValue 'rows'
*/
layout?: AdaptableFormLayout;
/**
* Optional form-level submit hook.
*
* Fired when the user presses `Enter` while focus is inside any field
* input AND the form is currently valid. The full `formData` is passed
* through, typed as `TData` if you've supplied a custom shape.
*
* Use this as an ergonomic shortcut to avoid wiring an explicit "Submit"
* button - or in addition to one, so power users can press Enter to
* commit. If the form is invalid the hook is not invoked.
*/
onSubmit?: (formData: TData, context: T) => void;
}
/**
* How an AdaptableForm's fields are visually arranged
*/
export type AdaptableFormLayout = 'rows' | 'inline';
/**
* Logical grouping of related fields, with an optional title and
* description. Use to break long forms into visually distinct sections.
*
* Groups behave like fields when it comes to `hidden`: a hidden group is
* not rendered and its child fields are excluded from validation. They
* are otherwise transparent - each child field continues to drive its
* own value, validation, and `onValueChange` lifecycle.
*
* In the `inline` layout the group's title/description are not rendered
* (the layout is too compact for sectioning). The child fields are
* laid out inline alongside the rest.
*/
export interface AdaptableFormFieldGroup {
/**
* Discriminator - identifies the item as a group rather than a field.
*/
kind: 'group';
/**
* Optional title rendered as the section header.
*/
title?: string;
/**
* Optional description rendered immediately below the title.
*/
description?: string;
/**
* Fields contained in the group. Inner items may be a single field or
* an array of fields (rendered side-by-side on the same row, exactly
* like the top-level shorthand).
*/
fields: (AdaptableFormField | AdaptableFormField[])[];
/**
* Hide the entire group. Either a boolean or a function evaluated
* against the current form data. Hidden groups skip rendering and
* exclude their children from validation.
*/
hidden?: boolean | ((formData: AdaptableFormData, context: BaseContext) => boolean);
}
/**
* Defines a Field that appears in an Adaptable Form
*/
export interface AdaptableFormField {
/**
* Name of the Field
*/
name: string;
/**
* Label to display in the Field
*/
label: string;
/**
* Field Type - dictates which UI control is rendered
*/
fieldType: AdaptableFormFieldType;
/**
* Field Default Value - can be of type string, boolean, number, or for
* `select` fields with `multi: true` an array of those.
*
* For single `select` fields, this is also the value restored when the user
* clears the combobox (unless {@link clearToDefault} is `false`).
*/
defaultValue?: string | boolean | number | Array<string | number | boolean>;
/**
* For single `select` fields only. When the user clears the combobox,
* whether to restore {@link defaultValue} instead of leaving the field empty.
*
* Defaults to `true` when `defaultValue` is set, otherwise `false`.
* Set to `false` when an empty selection is a distinct state from the default.
*/
clearToDefault?: boolean;
/**
* Items to populate the `select` and `radio` fieldTypes.
*
* Either a static array, or a function that derives the options from the
* current form data and Adaptable context. The function form may return
* a Promise for asynchronously-loaded options - the form will render the
* field with no items until the promise resolves.
*/
options?: AdaptableFormFieldOption[] | ((formData: AdaptableFormData, context: BaseContext) => AdaptableFormFieldOption[] | Promise<AdaptableFormFieldOption[]>);
/**
* For `select` fields only. When `true` the field renders as a
* multi-select combobox; the field's value is an array of selected
* `option.value`s rather than a single value.
*
* @defaultValue false
*/
multi?: boolean;
/**
* For `custom` fields only. Renders an arbitrary React node as the
* field's input. Receives the current value, a `setValue` setter
* (controlled), the current form data, the Adaptable context and the
* resolved `disabled` state.
*
* Use this as an escape hatch when none of the built-in `fieldType`s
* fit - e.g. to render a star-rating widget, a tag editor or any
* bespoke control.
*/
render?: (params: AdaptableFormFieldRenderParams) => React.ReactNode;
/**
* Placeholder text shown inside `text`, `textarea`, `number`, `date`,
* `time` and `datetime` inputs when empty. For `select` fields it is
* rendered as the empty-state label when no value is selected.
*
* When omitted on a `select` field with a {@link defaultValue}, the label of
* the matching `options` entry is used as the empty-state label.
*/
placeholder?: string;
/**
* Inline help text rendered immediately below the field's input. Useful
* for short explanations such as "format: YYYY-MM-DD" or "min 3 chars".
*/
helpText?: string;
/**
* Tooltip shown when hovering the field's label.
*/
tooltip?: string;
/**
* Hide the field. Either a boolean or a function evaluated against the
* current form data. Hidden fields are not rendered and are excluded from
* validation.
*/
hidden?: boolean | ((formData: AdaptableFormData, context: BaseContext) => boolean);
/**
* Disable the field's input. Either a boolean or a function evaluated
* against the current form data. Disabled fields render but the user
* cannot edit them.
*/
disabled?: boolean | ((formData: AdaptableFormData, context: BaseContext) => boolean);
/**
* If `true` the field is treated as required. An empty value (`''`,
* `null`, `undefined`, or unchecked checkbox) will produce a validation
* error.
*/
required?: boolean;
/**
* Custom field-level validation. Return an error message string to mark
* the field invalid, or `null`/`undefined` if the value is valid.
*
* Custom validation runs *after* built-in checks (`required`, `min`,
* `max`, `pattern`, etc.) - the first failing check wins.
*/
validate?: (value: any, formData: AdaptableFormData, context: BaseContext) => string | null | undefined;
/**
* Minimum allowed value. For `number` and `slider`: the lowest accepted
* number. For `date`, `time` and `datetime`: the earliest allowed value
* in ISO format (`YYYY-MM-DD` / `HH:mm` / `YYYY-MM-DDTHH:mm`).
*/
min?: number | string;
/**
* Maximum allowed value. For `number` and `slider`: the highest accepted
* number. For `date`, `time` and `datetime`: the latest allowed value in
* ISO format.
*/
max?: number | string;
/**
* Stepping interval for `number`, `slider`, `time` and `datetime`
* fields.
*/
step?: number;
/**
* Minimum length for `text` and `textarea` fields.
*/
minLength?: number;
/**
* Maximum length for `text` and `textarea` fields.
*/
maxLength?: number;
/**
* Regular expression that the value of a `text` field must match.
*/
pattern?: string;
/**
* For `textarea` fields: the visible number of text rows.
*
* @defaultValue 3
*/
rows?: number;
/**
* Optional callback invoked whenever value of this field changes
*/
onValueChange?: (value: any, context: BaseContext) => void;
}
/**
* Map of validation errors produced by {@link validateAdaptableForm},
* keyed by field `name`. A field is valid when its key is not present.
*/
export type AdaptableFormErrors = Record<string, string>;
/**
* Types of Controls used in an AdapTable Form (standard form set)
*/
export type AdaptableFormFieldType = 'text' | 'textarea' | 'select' | 'radio' | 'date' | 'time' | 'datetime' | 'number' | 'slider' | 'color' | 'checkbox' | 'textOutput' | 'custom';
/**
* A single option used by a `select` or `radio` field.
*/
export interface AdaptableFormFieldOption {
value: any;
label: string;
}
/**
* Parameters provided to {@link AdaptableFormField.render} when rendering
* a `custom` field type.
*/
export interface AdaptableFormFieldRenderParams {
/**
* The field's current value
*/
value: any;
/**
* Setter to update the field's value. Triggers form-level `onChange`,
* per-field `onValueChange` and re-validation.
*/
setValue: (newValue: any) => void;
/**
* The field definition itself - useful when sharing one render function
* across several `custom` fields.
*/
field: AdaptableFormField;
/**
* The current values of all fields in the form, keyed by field `name`.
*/
formData: AdaptableFormData;
/**
* Adaptable context (carries `adaptableApi`, etc.). When the form is
* hosted by a Custom Toolbar this is in fact a
* `CustomToolbarFormContext`.
*/
context: BaseContext;
/**
* Resolved disabled state of the field (after evaluating `field.disabled`
* if it's a function).
*/
disabled: boolean;
/**
* Current validation error for this field, if any.
*/
error?: string;
}
/**
* Returns true if the supplied entry from a form's `fields` array is a
* group rather than a single field or an inline-row array of fields.
*/
export declare function isAdaptableFormFieldGroup(entry: AdaptableFormField | AdaptableFormField[] | AdaptableFormFieldGroup): entry is AdaptableFormFieldGroup;
/**
* Flattens a form's `fields` array (which may contain field groups and
* inline-row arrays) into a single flat list of fields. Useful when
* computing defaults, validation or per-field lookups.
*/
export declare function flattenAdaptableFormFields<T extends BaseContext>(formDef?: AdaptableForm<T, any>): AdaptableFormField[];
export declare function shouldClearSelectToDefault(field: AdaptableFormField): boolean;
export declare function resolveSelectValueAfterClear(field: AdaptableFormField, newValue: unknown): unknown;
export declare function resolveSelectPlaceholder(field: AdaptableFormField, options: AdaptableFormFieldOption[]): string | undefined;
export declare function getDefaultAdaptableFormData<T extends BaseContext = BaseContext>(formDef?: AdaptableForm<T, any>): AdaptableFormData;
/**
* Resolves a field's `options` to the current array of options.
*
* - When `options` is already an array, it's returned as-is.
* - When it's a synchronous function, the function is invoked.
* - When the function returns a Promise this helper returns `undefined` -
* asynchronous resolution is the caller's responsibility (the
* `AdaptableFormComponent` resolves promises via internal state).
*/
export declare function resolveAdaptableFormFieldOptionsSync(field: AdaptableFormField, formData: AdaptableFormData, context: BaseContext): AdaptableFormFieldOption[] | undefined;
/**
* Returns true if the given field should be hidden given the current form
* data and context. Resolves both boolean and function forms of `hidden`.
*/
export declare function isAdaptableFormFieldHidden(field: AdaptableFormField, formData: AdaptableFormData, context: BaseContext): boolean;
/**
* Returns true if the given group should be hidden given the current
* form data and context.
*/
export declare function isAdaptableFormFieldGroupHidden(group: AdaptableFormFieldGroup, formData: AdaptableFormData, context: BaseContext): boolean;
/**
* Returns true if the given field should be disabled given the current
* form data and context. Resolves both boolean and function forms of
* `disabled`.
*/
export declare function isAdaptableFormFieldDisabled(field: AdaptableFormField, formData: AdaptableFormData, context: BaseContext): boolean;
/**
* Validates an entire AdaptableForm. Hidden fields and fields inside
* hidden groups are skipped. Returns a map of field-name to error
* message; fields without entries are valid.
*
* Validation order per field: `required` -> built-in (min/max/length/
* pattern) -> custom `validate`. The first failing check wins.
*/
export declare function validateAdaptableForm<T extends BaseContext = BaseContext>(formDef: AdaptableForm<T, any>, formData: AdaptableFormData, context: BaseContext): AdaptableFormErrors;