import { ElementStyles } from '@microsoft/fast-element'; import { GenesisElement } from '@genesislcap/web-core'; import { ViewTemplate } from '@microsoft/fast-element'; /** * Basic `AND` and `OR` logical combinators which can be used in the model config. * @beta * */ declare const BASE_LOGICAL_COMBINATORS: Combinator[]; /** @alpha **/ declare const BASE_OPERATORS: Operator[]; /** * An operator which has one value. Example `greater_than` * @beta **/ declare type BinaryOperator = { nbInputs: 1; } & _Operator; /** * Configuration for a boolean-type input, which has a boolean value and a checkbox. * @beta **/ declare type CheckboxInput = { input: 'checkbox'; type: 'boolean'; }; /** * A combinator is a type of expression which can link two or more rules or groups. The most simple example is `AND` and `OR` which * can take any number of expressions to produce one boolean value. * @beta * * `type`: e.g., 'AND', 'OR', 'XOR', 'MULTIPLY', etc. * * `maxRules`: Controls number of nested rules. Max 1 only allows 1 rule, Max 2 allows 2 rules/groups, many allows any rules/groups * * `invisible`: If true then it won't appear on the component UI and is only selectable programmatically, or by default if it's the first and only value. If the only combinators are invisible then you're configuring the expression builder to not use combinators. Use the {@link NULL_COMBINATOR} for this purpose. * * `label`: Optional: Display label (if different from type) * * (not yet implemented) `description`: Optional: More details about the combinator * * (not yet implemented) `allowedFields`: Array of allowed field IDs (or `null` for all) * * (not yet implemented) `allowedOperators`: Explicit array of allowed operator types. (or `null` for all) * * (not yet implemented) `validator`: Optional: A validation function for the whole group. Null is valid, string is error message * * (not yet implemented) `allowLiterals`: Allow literal values instead of having to select a field * * (not yet implemented) `forceFieldOnly`: If the user selects a field they use it for it's value, without an operation on it */ /** @beta **/ declare type Combinator = { type: string; maxRules: 1 | 2 | 'many'; invisible?: boolean; label?: string; description?: string; allowedFields?: Field['fieldId'][]; allowedOperators?: Operator['type'][]; validator?: (group: Group) => string | null; allowLiterals?: boolean; forceFieldOnly?: boolean; }; declare namespace Config { export { BASE_LOGICAL_COMBINATORS, LOGICAL_COMBINATORS, NULL_COMBINATOR, BASE_OPERATORS } } export { Config } /** * @beta * The configuration object to set on an expression builder object. The expression builder always requires `operators`, * `fields` and `combinators` to be configured. * * Specific implementations of the expression builder may expose these options via a different attribute and automatically * configure some options. E.g. a rule (boolean) expression builder may configure boolean operators and combinators so the user * doesn't have to. * * `operators`: Array of {@link Operator} objects * * `fields`: Array of {@link Field} objects * * `combinators`: Array of {@link Combinator} objects * * `model`: Optional {@link Group} model to hydrate * * `maxNesting`: If set and more than 0, is a 1-indexed config for the maximum nesting level of groups. * * `partialRuleValidationWarning`: If set a rule will highlight in an error colour when it's not complete (doesn't have a field + operator + (maybe value) selected) */ /** @beta **/ declare type Config_2 = { operators: Operator[]; fields: Field[]; combinators: Combinator[]; model?: Group; maxNesting?: number; partialRuleValidationWarning?: boolean; }; /** @beta * By default the expression builder uses the basic html components such as buttons and inputs. If you want to integrate your * own components from a design system or otherwise you can add the tag names for your elements here. For your custom components * to work they must expose the same API as the underlying HTML element they're overriding. * * `checkbox`: Custom element tag for checkbox inputs * * `text`: Custom element tag for text inputs * * `number`: Custom element tag for number inputs * * `date`: Custom element tag for date inputs * * `datetimeLocal`: Custom element tag for datetime-local inputs * * `select`: Custom element tag for select inputs * * `option`: Custom element tag for option elements * * `button`: Custom element tag for button elements * * `radio`: Custom element tag for radio and radio group elements. When using a custom element for a radio you require a parent radio group component to semantically link the radios into a group. The parent radio group must expose the `change` event and `value` attribute. */ declare type CustomElements = { checkbox?: string; text?: string; number?: string; date?: string; datetimeLocal?: string; select?: string; option?: string; optgroup?: string; button?: string; radio?: { input: string; group: string; }; }; /** * @beta * Optional strings for configuring css to be applied inside of each constituent element's shadow DOM. * To apply styles to components which are used inside of multiple different components (such as buttons which are used in groups, * rules, and values) you must ensure the styling is set in each block. If your styling isn't showing the ensure that you're * using more specific css rules to override the precedence of your rule. * * `rule`: Additional CSS for expression rule component * * `value`: Additional CSS for rule value component * * `field`: Additional CSS for rule field component * * `operator`: Additional CSS for rule operator component * * `group`: Additional CSS for expression group component * * @privateRemarks * Use css layers to help with the precedence in future. */ declare type CustomStyles = { rule?: string; value?: string; field?: string; operator?: string; group?: string; }; /** * Configuration for a date input, which has s string value and a date field input * @beta **/ declare type DateInput = { input: 'date'; type: 'date'; validation?: (x: unknown) => string | null; }; /** * Configuration for a datetime input, which has s string value and a datetime field input * @beta **/ declare type DateTimeInput = { input: 'datetime-local'; type: 'date-time'; validation?: (x: unknown) => string | null; }; /** * Top level component to allow the user to build expressions. It produces a generic payload which doesn't have any system by itself * to evaluate or execute the built expression. * * The basics required to work with this component: * * {@link ExpressionBuilder.config} property to configure and input data and models into the component. * * _Event_ change - `Types.Group` emits the model configuration on change. If you create a child component of the expression builder * where you want to use a different (e.g. domain specific) model then it will likely override then emit event and instead emit * it's own model. To check the underlying `Types.Group` model check the {@link ExpressionBuilder.model} property. * * @fires change - Fired when the expression model changes * @fires add-group - Bubbled when a nested group add is requested * @fires del-group - Bubbled when a group delete is requested * @fires add-rule - Bubbled when a rule add is requested * @fires del-rule - Bubbled when a rule delete is requested * @fires update-group - Bubbled when group data changes * @fires update-rule - Bubbled when rule data changes * * @beta */ export declare class ExpressionBuilder extends GenesisElement implements MetadataProvider { /** * config - `Types.Config` the configuration which is required to be set for the expression builder. All properties are * defined under this single object to enforce that they're kept in sync with one another. * * If you want to set the expression of the expression builder you should do it via the `model` property on this object. * * If you're using a child class of this component with a specific model implementation you likely *don't* want to set this * property directly. See example 3. * * @example * Configuring the basic elements required by an expression builder instance * ```ts * const config: Types.Config = { * fields: ..., * combinators: ..., * operators: ..., * }; * document.querySelector('expression-builder').config = config; * ``` * * @example * Configuring the basic elements required by an expression builder instance, as well as inputting a model to hydrate * ```ts * const config: Types.Config = { * fields: ..., * combinators: ..., * operators: ..., * model: ..., * }; * document.querySelector('expression-builder').config = config; * ``` * * @example * You may create your own child of the expression builder which automatically defines some of the properties, such * as creating a rule builder which defines boolean operators and combinators. In this case you should use your own * property name, and on change apply the user and your configurations back to the config property. * ```ts * const config: MyTypes.RuleConfig = { * operators: ..., * }; * // In the implementation of RuleExpressionBuilder it should listen to ruleConfigChanged and * apply the missing combinators and opreators back to the config along with the user's configuration * document.querySelector('rule-expression-builder').ruleConfig = config; * ``` * @beta */ config: Config_2; /** * styles - `Types.Styles` optional configuration which allows you to set custom element tag names to be used, as well as * custom css to be inserted into the shadow DOM. * @example * Bare bones example of using a custom select component * ```ts * const styles: Types.Styles = { * customElements: { * select: 'rapid-select', * }, * styles: { * // If you want to customise your `rapid-select` then you can do it here. The select input * // is used in the value, field, and operator component, so you should set the styles for all of them for real * value: ` * rapid-select { * max-width: 180px; * } * ` * } * } * document.querySelector('expression-builder').styles = styles; * ``` * * @beta */ styles?: Styles; /** * model - `Types.Group` the current model which completely describes the state of the component. * * *IMPORTANT* you should not set this yourself via this property directly, you should always set it via the model property on the {@link ExpressionBuilder.config} block. * * You may want to read from this variable to get the most up to date state, for example if you create a child component which * has a model which isn't valid for every single state (e.g. requires a complete rule) you can check this underlying model to * verify what field or operator is selected. * * @beta * * @example * ```ts * const model = document.querySelector('expression-builder'); * // Once you have the model you can read it to check the applied config. While the primary use should be checking the updated model via * the emited change event, you can use this to check more specific changes that a domain specific model might not. * * For example, imagine a RulExpressionBuilder which is an implementation specifically for a boolean logic expression. That component may not * model a non-complete rule (a rule without a field, and operator, and value). In that case when it emits the event the payload will only change * when the user has completely configured a new rule. But if you need to catch cases earlier when they've changed the field but before they've * changed the value you can check the model here. * ``` */ model: ModelGroup | null; private ruleCount; private groupCount; /** * @internal * We don't want to dispatch a change event for the model if it's just changed because * the user updated it themselves, otherwise if they automatically update the config property * on change it'll get into an infinite loop */ private userUpdatingModel; /** @internal */ configChanged(_: Config_2, newConfig: Config_2): void; /** @internal */ modelChanged(_: Config_2, newModel: ModelGroup): void; /** * @beta * Dispatches the provided model to the DOM. * * @remarks * Override this to change the shape of model the component returns */ protected dispatchChangeEvent(group: Group): void; /** @internal */ getConfig(): Config_2; /** @internal */ getGroupId(): string; /** @internal */ getRuleId(): string; /** @internal */ connectedCallback(): void; /** @internal */ disconnectedCallback(): void; private isValidConfig; private initBaseModel; private handleAddGroup; private _handleAddGroup; private handleAddRule; private _handleAddRule; private handleDeleteGroup; private _handleDeleteGroup; private handleDeleteRule; private _handleDeleteRule; private handleUpdateGroupData; private _handleUpdateGroupData; private handleUpdateRuleData; private _handleUpdateRuleData; } /** * Configuration for the fields which the user can choose from per rule, the left hand side of each rule expression. * @beta * * `optgroup`: Group the fields inside of the select component. This option currently isn't supported if you're using custom elements configured with {@link CustomElements}. * * `defaultValue`: Default value for the field. If set then the value of any rule operands is defaulted to this value, but the user can change the value to something else. * * `fieldId`: Id for the field * * `label`: Label for the field, what the user sees on the select input listbox. * * (not yet implemented) `operators`: Constrain the operators which can be used with this field (e.g. only allow starts_with on a string) */ /** @beta **/ declare type Field = { optgroup?: string | null; defaultValue?: any; fieldId: string; label: string; operators?: string[]; } & FieldTypes; /** * Union of all input types * @beta **/ declare type FieldTypes = TextInput | NumberInput | CheckboxInput | SelectInput | DateInput | DateTimeInput; /** * Gets the string representation from a `Date` which is the format a `date` input uses. * `yyyy-mm-dd`. * Uses UTC methods to ensure consistent output regardless of timezone. * * Used to convert a javascript date object into the required string format expected by the expression builder. * @beta */ export declare const formatDateString: (date: Date) => string; /** * Gets the string representation from a `Date` which is the format a `datetime-local` input uses. * `yyyy-mm-ddThh:mm:ss`. * Uses UTC methods to ensure consistent output regardless of timezone. * * Used to convert a javascript date object into the required string format expected by the expression builder. * @beta */ export declare const formatDateTimeString: (date: Date) => string; /** * A group forms the overall model of the expression builder, and is recursive to itself allowing for a nested tree. * * @example * ``` * RULE 1 * FIELD : PROFILE_AGE * OPERATOR : GREATER_THAN * VALUE : 18 * * COMBINATOR : OR * * GROUP 2 * RULE 2 * FIELD : PARENT_ROLE * OPERATOR : ONE_OF * VALUE : GIVES_PERMISSION * * COMBINATOR : AND * * RULE 3 * FIELD : GRANTS_CHILD_ACCESS * OPERATOR : EQUALS * VALUE : true * * If you're constructing a boolean expression then this rule could be used to restrict users who are 17 or younger, but * allowing parents who have specific rights to be able to grant access too. * ``` * @beta */ declare type Group = { combinator: Combinator; children: (Rule | Group)[]; }; /** @internal **/ declare type GroupMetadata = { groupId: string; level: number; config: Config_2; }; /** @internal **/ declare type GroupStructure = Omit & { children: (ModelRule | ModelGroup)[]; }; /** * A set of boolean logic combinators which configure the expression builder to produce boolean expressions. * @beta * */ declare const LOGICAL_COMBINATORS: Combinator[]; /** @internal */ declare interface MetadataProvider { getGroupId(): string; getRuleId(): string; getConfig(): Config_2; } /** @internal **/ declare type ModelGroup = GroupMetadata & GroupStructure; /** @internal **/ declare type ModelRule = Rule & RuleMetadata; /** * A combinator which has a null action. * * If configured as the only combinator then it configures the expression builder to only create a single rule. * @beta * */ declare const NULL_COMBINATOR: Combinator; /** * Configuration for a number-type input, which has a number value and a number input. * @beta **/ declare type NumberInput = { input: 'number'; type: 'int' | 'short' | 'double' | 'long' | 'bigdecimal'; validation?: (x: unknown) => string | null; }; /** @beta **/ declare type Operator = UniraryOperator | BinaryOperator | TernararyOperator | VariadicOperator; /** * The shared configuration properties of an operator * @beta * * `applyTo`: Only allow this operator to be used on a field type (e.g. only allow this on an enum) * * `optgroup`: Group the operators inside of the select component * * `type`: The name of the operator (e.g. one_of) * * `label`: Optional: Display label (if different from type) * * `tooltip`: Optional tooltip to display on mouse hover * * `valueType`: [EXPERIMENTAL] Optional override to change the input type for the operator (e.g. a date field could present a string input) */ /** @beta **/ declare type _Operator = { applyTo: FieldTypes['type'][]; optgroup?: string | null; type: string; label?: string; tooltip?: string; valueType?: FieldTypes; }; /** * A rule is a single constituent element of a larger expression, and is the smallest whole part of an expression. * * @example * ``` * FIELD : PROFILE_AGE * OPERATOR : GREATER_THAN * VALUE : 18 * * If you're constructing a boolean expression then this rule could be used to restrict users who are 17 or younger. * ``` @beta **/ declare type Rule = { field: Field | null; } & ({ operator: null; } | { operator: UniraryOperator; } | { operator: BinaryOperator; value: any; } | { operator: TernararyOperator; value: [any, any]; } | { operator: VariadicOperator; value: any[]; }); /** @internal **/ declare type RuleMetadata = { ruleId: string; config: Config_2; }; /** * Configuration for an enum-type input, which as a string or number value, and uses a select input. * @beta **/ declare type SelectInput = { input: 'select'; type: 'enum'; values: Record; }; /** * Configuration items for the expression builder styles. * * `customElements`: optional `Types.CustomElements` block for overriding the html tags used in the expression builder * * `customStyles`: optional `Types.CustomStyles` block to configure custom css for components. * * @beta **/ declare type Styles = { customElements?: CustomElements; customStyles?: CustomStyles; }; /** @alpha **/ export declare const styles: ElementStyles; /** @alpha **/ export declare const template: ViewTemplate; /** * An operator which has two values. Example `between_inclusive` * @beta **/ declare type TernararyOperator = { nbInputs: 2; } & _Operator; /** * Configuration for a text-type input, which has a string value and a text input. * @beta **/ declare type TextInput = { type: 'string'; input: 'text'; validation?: (x: unknown) => string | null; }; declare namespace Types { export { CustomElements, CustomStyles, Styles, Config_2 as Config, _Operator, UniraryOperator, BinaryOperator, TernararyOperator, VariadicOperator, Operator, TextInput, NumberInput, CheckboxInput, SelectInput, DateInput, DateTimeInput, FieldTypes, Field, Combinator, Rule, Group } } export { Types } /** * An operator which doesn't have any value. Example `is_null` * @beta * **/ declare type UniraryOperator = { nbInputs: 0; } & _Operator; /** * An operator which can have any number of values where `NumVals >= 1`, defaulting to 1. Example `one_of`. * @beta **/ declare type VariadicOperator = { nbInputs: 'many'; } & _Operator; export { }