import { getPropertyControlTypes } from "components/propertyControls"; import type { ValidationResponse, ValidationTypes, } from "constants/WidgetValidation"; import type { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; import type { CodeEditorExpected } from "components/editorComponents/CodeEditor"; import type { UpdateWidgetPropertyPayload } from "actions/controlActions"; import type { AdditionalDynamicDataTree } from "utils/autocomplete/customTreeTypeDefCreator"; import type { Stylesheet } from "entities/AppTheming"; import type { ReduxActionType } from "ee/constants/ReduxActionConstants"; import type { PropertyUpdates } from "WidgetProvider/constants"; import type { WidgetProps } from "widgets/BaseWidget"; const ControlTypes = getPropertyControlTypes(); export type ControlType = (typeof ControlTypes)[keyof typeof ControlTypes]; export interface PropertyPaneSectionConfig { /** * Title displayed at the top of a collapsible section in the property pane. */ sectionName: string; /** * Unique identifier for the section. Used for: * - Managing section collapse/expand state in Redux * - Navigation and search functionality in the property pane * - React rendering optimization (as key) * * If not provided, it will be auto-generated during widget initialization. */ id?: string; /** * Array of properties that should go inside the section. */ children: PropertyPaneConfig[]; /** * If false, the section will not be collapsible. */ collapsible?: boolean; /** * A unique id generated by combining the ids of all the children. * Mainly used in memoization to prevent unnecessary re-renders of property sections ( PropertySection.tsx ) */ childrenId?: string; /** * Callback function to determine if the section should be hidden. * * @param props - Current widget properties * @param propertyPath - Path to the widget property * @returns - True if the section should be hidden, false otherwise */ // TODO: Fix this the next time the file is edited // eslint-disable-next-line @typescript-eslint/no-explicit-any hidden?: (props: any, propertyPath: string) => boolean; /** * when true, the section will be open by default. * Note: Seems like this is not used anywhere. */ isDefaultOpen?: boolean; propertySectionPath?: string; /** * Used to show a tag right after the section name (usedonly in the search results) */ tag?: string; /** * Indicates whether this section contains properties that need to be generated dynamically at runtime. * Used in conjunction with generateDynamicProperties. * * Common use cases: * - Dynamic event handlers ( CustomWidget's ) * - Properties that depend on user configuration * - Properties that need to be generated based on widget state */ hasDynamicProperties?: boolean; /** * Function to generate property controls dynamically at runtime. * Called when hasDynamicProperties is true. * * @param widget - Current widget properties * @returns Array of dynamically generated property controls * * @example * Used in CustomWidget and ExternalWidget to dynamically generate event handler properties * based on available events. */ generateDynamicProperties?: ( widget: WidgetProps, ) => PropertyPaneControlConfig[]; expandedByDefault?: boolean; } export interface PanelConfig { editableTitle: boolean; titlePropertyName: string; panelIdPropertyName: string; children?: PropertyPaneConfig[]; contentChildren?: PropertyPaneConfig[]; styleChildren?: PropertyPaneConfig[]; searchConfig?: PropertyPaneConfig[]; // A combination of contentChildren and contentChildren which will be used to display search results updateHook: ( // TODO: Fix this the next time the file is edited // eslint-disable-next-line @typescript-eslint/no-explicit-any props: any, propertyPath: string, // TODO: Fix this the next time the file is edited // eslint-disable-next-line @typescript-eslint/no-explicit-any propertyValue: any, ) => Array | undefined; } export interface PropertyPaneControlConfig { /** * Unique identifier for the control. Used for internal tracking and debugging. * It added by `addPropertyConfigIds` function in `WidgetProvider/factory/helpers.ts`. */ id?: string; /** * Label shown above the control. */ label: string; /** * Human-readable slugified name of the property. Used to identify the property in the widget properties. */ propertyName: string; /** * Used to provide more context about the property. Appears as tooltip when we hover over the label of the property. * Note: This is different from `helperText` which appears below the property input. */ helpText?: string; /** * Dynamic text that appears below the property input. */ helperText?: ((props: unknown) => React.ReactNode) | React.ReactNode; /** * If true, transform the property input into plain input where js can be written.. */ isJSConvertible?: boolean; /** * Used in special cases where we want to custom control instead regular text input control. * For example: `COMPUTE_VALUE` control is used in table widget provides access to currentRow in controls in the table columns. */ customJSControl?: string; /** * Type of the property control. * Full list of supported controls can be found in `app/client/src/components/propertyControls/index.ts`. */ controlType: ControlType; /** @deprecated. Not used anywhere. */ validationMessage?: string; /** * path that is used to get evaluated value for the property. */ dataTreePath?: string; /** * There is no requirement to define children for a control. This is just done to suffice types * in places where controlConfig and sectionConfig are not differentiated. */ children?: PropertyPaneConfig[]; panelConfig?: PanelConfig; /** * Callback function to update related widget properties. * * @param propertyName - Path to the widget property * @param propertyValue - New value of the property * @param props - Current widget properties * @returns - Array of property updates * * @example Used in tabs widget to update the label of the tab. */ updateRelatedWidgetProperties?: ( propertyName: string, // TODO: Fix this the next time the file is edited // eslint-disable-next-line @typescript-eslint/no-explicit-any propertyValue: any, // TODO: Fix this the next time the file is edited // eslint-disable-next-line @typescript-eslint/no-explicit-any props: any, ) => UpdateWidgetPropertyPayload[]; /** * Function that is called when the property is updated, it is mainly used to update other properties * * @param props - Current widget properties * @param propertyName - Path to the widget property * @param propertyValue - New value of the property * * @returns - Array of property updates */ updateHook?: ( // TODO: Fix this the next time the file is edited // eslint-disable-next-line @typescript-eslint/no-explicit-any props: any, propertyName: string, // TODO: Fix this the next time the file is edited // eslint-disable-next-line @typescript-eslint/no-explicit-any propertyValue: any, ) => Array | undefined; /** * callback function to determine if the property should be hidden. * @param props - Current widget properties * @param propertyPath - Path to the widget property * @returns - True if the property should be hidden, false otherwise */ // TODO: Fix this the next time the file is edited // eslint-disable-next-line @typescript-eslint/no-explicit-any hidden?: (props: any, propertyPath: string) => boolean; /** * If true, the property is hidden. * Note: hidden and invisible do the same thing but differently. hidden uses a callback to determine if the property should be hidden. * invisible is a boolean flag to hide the property. */ invisible?: boolean; isBindProperty: boolean; /** * If true, it means the property triggers a widget action. * * @example * OnClick property in Button widget is a trigger property that can trigger an action. */ isTriggerProperty: boolean; /** * Validation configuration for the property */ validation?: ValidationConfig; /** * Callback function to provide additional autocomplete data for the autocomplete list. * * @param props - Current widget properties * @returns - Additional autocomplete data */ // TODO: Fix this the next time the file is edited // eslint-disable-next-line @typescript-eslint/no-explicit-any additionalAutoComplete?: (props: any) => AdditionalDynamicDataTree; evaluationSubstitutionType?: EvaluationSubstitutionType; /** * All properties that current property is dependent on. * All of these properties becomes available in `widgetProperties` in the property control.. */ dependencies?: string[]; /** * It is same as `dependencies` but these dependencies are not statically defined in the widget. * The callback is called during rendering of the property control to get the latest dependencies. * * @example * Used in CustomWidget to dynamically get the dependencies based on the current state of the widget. */ dynamicDependencies?: (widget: WidgetProps) => string[]; /** * Dependencies to be picked from the __evaluated__ object */ evaluatedDependencies?: string[]; expected?: CodeEditorExpected; /** * Used to get value of the property from stylesheet config. Used in app theming v1 ( Not needed in anvil ) * * @param props - Current widget properties * @param propertyPath - Path to the widget property * @param stylesheet - Stylesheet config * @returns - Value of the property */ getStylesheetValue?: ( // TODO: Fix this the next time the file is edited // eslint-disable-next-line @typescript-eslint/no-explicit-any props: any, propertyPath: string, stylesheet?: Stylesheet, ) => Stylesheet[string]; /** * Options for certain controls like Dropdown Control * Note: This should be moved to controlConfig instead of being a top level property. */ // TODO(abhinav): To fix this, rename the options property of the controls which use this // Alternatively, create a new structure // TODO: Fix this the next time the file is edited // eslint-disable-next-line @typescript-eslint/no-explicit-any options?: any; // The following should ideally be used internally postUpdateAction?: ReduxActionType; /** * If true, the property is a panel property ( that is nested property pane ) */ isPanelProperty?: boolean; // Switch mode ( JS -> Text ) shouldSwitchToNormalMode?: ( isDynamic: boolean, isToggleDisabled: boolean, triggerFlag?: boolean, ) => boolean; /** * `controlConfig` is a generic record that can be used to pass additional configuration * options to the property control. The specific structure and contents of this record * will depend on the control type and its individual requirements. */ controlConfig?: Record; /** * The default value of the property. */ defaultValue?: unknown; /** * If the property is marked reusable, on the next drop it will use the value of the last dropped widget. */ isReusable?: boolean; } interface ValidationConfigParams { /** * Minimum allowed value for a number. */ min?: number; /** * Maximum allowed value for a number. */ max?: number; /** * If true, the value must be a positive integer. */ natural?: boolean; /** * Default value for any type. */ default?: unknown; /** * If true or an array of strings, the value must be unique in an array. * If an array of strings is provided, it specifies particular paths that must be unique. */ unique?: boolean | string[]; /** * If true, the value is required. * Now used to check if the value is an empty string. */ required?: boolean; /** * If true, the key is required. */ requiredKey?: boolean; /** * Validator regex for text type. */ regex?: RegExp; /** * Allowed keys in an object type. */ allowedKeys?: Array<{ name: string; type: ValidationTypes; params?: ValidationConfigParams; }>; /** * Allowed values in a string and array type. */ allowedValues?: unknown[]; /** * Children configurations in an ARRAY or OBJECT_ARRAY type. */ children?: ValidationConfig; /** * Validation function for FUNCTION type. * * @param value - The value to validate * @param props - Current widget properties * @param _ - Additional parameter (unused) * @param moment - Moment.js instance * @returns - Validation response */ fn?: ( value: unknown, // TODO: Fix this the next time the file is edited // eslint-disable-next-line @typescript-eslint/no-explicit-any props: any, // TODO: Fix this the next time the file is edited // eslint-disable-next-line @typescript-eslint/no-explicit-any _?: any, // TODO: Fix this the next time the file is edited // eslint-disable-next-line @typescript-eslint/no-explicit-any moment?: any, ) => ValidationResponse; /** * AUTO GENERATED, SHOULD NOT BE SET BY WIDGET DEVELOPER */ fnString?: string; /** * FUNCTION type expected type and example. */ expected?: CodeEditorExpected; /** * If true, enables strict string validation of TEXT type. */ strict?: boolean; /** * If true, ignores the case of keys. */ ignoreCase?: boolean; /** * Used for ValidationType.ARRAY_OF_TYPE_OR_TYPE to define sub type. */ type?: ValidationTypes; /** * Used for ValidationType.UNION to define sub types. */ types?: ValidationConfig[]; /** * Used for ValidationType.ARRAY_OF_TYPE_OR_TYPE to define sub type params. */ params?: ValidationConfigParams; /** * Used for ValidationType.NUMBER to allow 0 to be passed through. Default value is true. */ passThroughOnZero?: boolean; /** * Used for ValidationType.TEXT to limit line breaks in a large JSON object. */ limitLineBreaks?: boolean; /** * Used for ValidationType.UNION when none of the union type validations succeed. */ defaultValue?: unknown; /** * Used for ValidationType.UNION when none of the union type validations succeed. */ defaultErrorMessage?: string; } export interface ValidationConfig { type: ValidationTypes; params?: ValidationConfigParams; } export type PropertyPaneConfig = | PropertyPaneSectionConfig | PropertyPaneControlConfig; export interface ActionValidationConfigMap { [configProperty: string]: ValidationConfig; }