@masterteam/components 0.0.92 → 0.0.93
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/masterteam-components-business-fields.mjs +2 -2
- package/fesm2022/masterteam-components-business-fields.mjs.map +1 -1
- package/fesm2022/masterteam-components-formula.mjs +985 -1
- package/fesm2022/masterteam-components-formula.mjs.map +1 -1
- package/fesm2022/masterteam-components-menu.mjs +2 -2
- package/fesm2022/masterteam-components-menu.mjs.map +1 -1
- package/fesm2022/masterteam-components.mjs +2 -0
- package/fesm2022/masterteam-components.mjs.map +1 -1
- package/package.json +1 -1
- package/types/masterteam-components-formula.d.ts +69 -2
- package/types/masterteam-components.d.ts +28 -1
|
@@ -1920,11 +1920,995 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImpor
|
|
|
1920
1920
|
args: [{ selector: 'mt-formula-status-bar', standalone: true, imports: [CommonModule], template: "<div\r\n class=\"flex items-center gap-4 border-t border-slate-200 px-4 py-2 text-sm dark:border-slate-700\"\r\n>\r\n <!-- Validation Status -->\r\n <div class=\"flex items-center gap-1.5\">\r\n @if (isValid()) {\r\n <span\r\n class=\"flex h-5 w-5 items-center justify-center rounded-full bg-emerald-100 text-emerald-600 dark:bg-emerald-900 dark:text-emerald-400\"\r\n >\r\n <svg\r\n class=\"h-3.5 w-3.5\"\r\n fill=\"none\"\r\n viewBox=\"0 0 24 24\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"2.5\"\r\n >\r\n <path\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n d=\"M5 13l4 4L19 7\"\r\n />\r\n </svg>\r\n </span>\r\n <span class=\"font-medium text-emerald-600 dark:text-emerald-400\">\r\n {{ labelValid() }}\r\n </span>\r\n } @else {\r\n <span\r\n class=\"flex h-5 w-5 items-center justify-center rounded-full bg-rose-100 text-rose-600 dark:bg-rose-900 dark:text-rose-400\"\r\n >\r\n <svg\r\n class=\"h-3.5 w-3.5\"\r\n fill=\"none\"\r\n viewBox=\"0 0 24 24\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"2.5\"\r\n >\r\n <path\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n d=\"M6 18L18 6M6 6l12 12\"\r\n />\r\n </svg>\r\n </span>\r\n <span class=\"font-medium text-rose-600 dark:text-rose-400\">\r\n {{ labelInvalid() }}\r\n </span>\r\n }\r\n </div>\r\n\r\n <!-- Separator -->\r\n <div class=\"h-4 w-px bg-slate-300 dark:bg-slate-600\"></div>\r\n\r\n <!-- Complexity -->\r\n <div class=\"flex items-center gap-1.5 text-slate-500 dark:text-slate-400\">\r\n <svg class=\"h-4 w-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\r\n <path\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n stroke-width=\"2\"\r\n d=\"M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z\"\r\n />\r\n </svg>\r\n <span>{{ labelComplexity() }}:</span>\r\n <span class=\"font-medium\" [class]=\"complexityClass()\">\r\n {{ complexity() }}\r\n </span>\r\n </div>\r\n\r\n <!-- Dependencies -->\r\n @if (dependencies().length > 0) {\r\n <div class=\"h-4 w-px bg-slate-300 dark:bg-slate-600\"></div>\r\n <div class=\"flex items-center gap-1.5 text-slate-500 dark:text-slate-400\">\r\n <svg\r\n class=\"h-4 w-4\"\r\n fill=\"none\"\r\n viewBox=\"0 0 24 24\"\r\n stroke=\"currentColor\"\r\n >\r\n <path\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n stroke-width=\"2\"\r\n d=\"M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1\"\r\n />\r\n </svg>\r\n <span>{{ labelDependencies() }}:</span>\r\n <span class=\"font-medium text-slate-700 dark:text-slate-300\">\r\n {{ dependenciesText() }}\r\n </span>\r\n </div>\r\n }\r\n\r\n <!-- Error message (if any) -->\r\n @if (firstError(); as error) {\r\n <div\r\n class=\"ms-auto flex items-center gap-1.5 text-rose-600 dark:text-rose-400\"\r\n >\r\n <svg\r\n class=\"h-4 w-4\"\r\n fill=\"none\"\r\n viewBox=\"0 0 24 24\"\r\n stroke=\"currentColor\"\r\n >\r\n <path\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n stroke-width=\"2\"\r\n d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"\r\n />\r\n </svg>\r\n <span class=\"text-xs\">{{ error.message }}</span>\r\n <span class=\"text-xs opacity-60\"\r\n >({{ error.line }}:{{ error.column }})</span\r\n >\r\n </div>\r\n }\r\n</div>\r\n" }]
|
|
1921
1921
|
}], propDecorators: { validation: [{ type: i0.Input, args: [{ isSignal: true, alias: "validation", required: false }] }], labels: [{ type: i0.Input, args: [{ isSignal: true, alias: "labels", required: false }] }] } });
|
|
1922
1922
|
|
|
1923
|
+
const CONDITION_FUNCTION_CATEGORIES = [
|
|
1924
|
+
{
|
|
1925
|
+
name: 'Logic',
|
|
1926
|
+
displayName: 'Field Visibility',
|
|
1927
|
+
functions: [
|
|
1928
|
+
{
|
|
1929
|
+
name: 'SHOW_IF',
|
|
1930
|
+
category: 'Logic',
|
|
1931
|
+
description: 'Show this field when condition is true',
|
|
1932
|
+
signature: 'SHOW_IF(condition)',
|
|
1933
|
+
parameters: [
|
|
1934
|
+
{
|
|
1935
|
+
name: 'condition',
|
|
1936
|
+
type: 'boolean',
|
|
1937
|
+
description: 'When true, field is visible',
|
|
1938
|
+
required: true,
|
|
1939
|
+
},
|
|
1940
|
+
],
|
|
1941
|
+
returnType: 'boolean',
|
|
1942
|
+
examples: ['SHOW_IF(@Status == "Active")'],
|
|
1943
|
+
},
|
|
1944
|
+
{
|
|
1945
|
+
name: 'HIDE_IF',
|
|
1946
|
+
category: 'Logic',
|
|
1947
|
+
description: 'Hide this field when condition is true',
|
|
1948
|
+
signature: 'HIDE_IF(condition)',
|
|
1949
|
+
parameters: [
|
|
1950
|
+
{
|
|
1951
|
+
name: 'condition',
|
|
1952
|
+
type: 'boolean',
|
|
1953
|
+
description: 'When true, field is hidden',
|
|
1954
|
+
required: true,
|
|
1955
|
+
},
|
|
1956
|
+
],
|
|
1957
|
+
returnType: 'boolean',
|
|
1958
|
+
examples: ['HIDE_IF(@Status == "Closed")'],
|
|
1959
|
+
},
|
|
1960
|
+
{
|
|
1961
|
+
name: 'DISABLE_IF',
|
|
1962
|
+
category: 'Logic',
|
|
1963
|
+
description: 'Disable this field when condition is true',
|
|
1964
|
+
signature: 'DISABLE_IF(condition)',
|
|
1965
|
+
parameters: [
|
|
1966
|
+
{
|
|
1967
|
+
name: 'condition',
|
|
1968
|
+
type: 'boolean',
|
|
1969
|
+
description: 'When true, field is disabled',
|
|
1970
|
+
required: true,
|
|
1971
|
+
},
|
|
1972
|
+
],
|
|
1973
|
+
returnType: 'boolean',
|
|
1974
|
+
examples: ['DISABLE_IF(@Status == "Approved")'],
|
|
1975
|
+
},
|
|
1976
|
+
{
|
|
1977
|
+
name: 'ENABLE_IF',
|
|
1978
|
+
category: 'Logic',
|
|
1979
|
+
description: 'Enable this field when condition is true',
|
|
1980
|
+
signature: 'ENABLE_IF(condition)',
|
|
1981
|
+
parameters: [
|
|
1982
|
+
{
|
|
1983
|
+
name: 'condition',
|
|
1984
|
+
type: 'boolean',
|
|
1985
|
+
description: 'When true, field is enabled',
|
|
1986
|
+
required: true,
|
|
1987
|
+
},
|
|
1988
|
+
],
|
|
1989
|
+
returnType: 'boolean',
|
|
1990
|
+
examples: ['ENABLE_IF(@Status == "Draft")'],
|
|
1991
|
+
},
|
|
1992
|
+
],
|
|
1993
|
+
},
|
|
1994
|
+
{
|
|
1995
|
+
name: 'Aggregation',
|
|
1996
|
+
displayName: 'Logic Helpers',
|
|
1997
|
+
functions: [
|
|
1998
|
+
{
|
|
1999
|
+
name: 'AND',
|
|
2000
|
+
category: 'Aggregation',
|
|
2001
|
+
description: 'Returns true if all conditions are true',
|
|
2002
|
+
signature: 'AND(condition1, condition2)',
|
|
2003
|
+
parameters: [
|
|
2004
|
+
{
|
|
2005
|
+
name: 'condition1',
|
|
2006
|
+
type: 'boolean',
|
|
2007
|
+
description: 'First condition',
|
|
2008
|
+
required: true,
|
|
2009
|
+
},
|
|
2010
|
+
{
|
|
2011
|
+
name: 'condition2',
|
|
2012
|
+
type: 'boolean',
|
|
2013
|
+
description: 'Second condition',
|
|
2014
|
+
required: true,
|
|
2015
|
+
},
|
|
2016
|
+
],
|
|
2017
|
+
returnType: 'boolean',
|
|
2018
|
+
examples: ['AND(@Status == "Active", @Priority > 0)'],
|
|
2019
|
+
},
|
|
2020
|
+
{
|
|
2021
|
+
name: 'OR',
|
|
2022
|
+
category: 'Aggregation',
|
|
2023
|
+
description: 'Returns true if any condition is true',
|
|
2024
|
+
signature: 'OR(condition1, condition2)',
|
|
2025
|
+
parameters: [
|
|
2026
|
+
{
|
|
2027
|
+
name: 'condition1',
|
|
2028
|
+
type: 'boolean',
|
|
2029
|
+
description: 'First condition',
|
|
2030
|
+
required: true,
|
|
2031
|
+
},
|
|
2032
|
+
{
|
|
2033
|
+
name: 'condition2',
|
|
2034
|
+
type: 'boolean',
|
|
2035
|
+
description: 'Second condition',
|
|
2036
|
+
required: true,
|
|
2037
|
+
},
|
|
2038
|
+
],
|
|
2039
|
+
returnType: 'boolean',
|
|
2040
|
+
examples: ['OR(@Status == "Active", @Status == "Pending")'],
|
|
2041
|
+
},
|
|
2042
|
+
{
|
|
2043
|
+
name: 'NOT',
|
|
2044
|
+
category: 'Aggregation',
|
|
2045
|
+
description: 'Returns the opposite of the condition',
|
|
2046
|
+
signature: 'NOT(condition)',
|
|
2047
|
+
parameters: [
|
|
2048
|
+
{
|
|
2049
|
+
name: 'condition',
|
|
2050
|
+
type: 'boolean',
|
|
2051
|
+
description: 'Condition to negate',
|
|
2052
|
+
required: true,
|
|
2053
|
+
},
|
|
2054
|
+
],
|
|
2055
|
+
returnType: 'boolean',
|
|
2056
|
+
examples: ['NOT(@IsCompleted)'],
|
|
2057
|
+
},
|
|
2058
|
+
{
|
|
2059
|
+
name: 'ISNULL',
|
|
2060
|
+
category: 'Aggregation',
|
|
2061
|
+
description: 'Returns true if value is null or empty',
|
|
2062
|
+
signature: 'ISNULL(value)',
|
|
2063
|
+
parameters: [
|
|
2064
|
+
{
|
|
2065
|
+
name: 'value',
|
|
2066
|
+
type: 'any',
|
|
2067
|
+
description: 'Value to check',
|
|
2068
|
+
required: true,
|
|
2069
|
+
},
|
|
2070
|
+
],
|
|
2071
|
+
returnType: 'boolean',
|
|
2072
|
+
examples: ['ISNULL(@Description)'],
|
|
2073
|
+
},
|
|
2074
|
+
{
|
|
2075
|
+
name: 'CONTAINS',
|
|
2076
|
+
category: 'Aggregation',
|
|
2077
|
+
description: 'Returns true if text contains the search string',
|
|
2078
|
+
signature: 'CONTAINS(text, search)',
|
|
2079
|
+
parameters: [
|
|
2080
|
+
{
|
|
2081
|
+
name: 'text',
|
|
2082
|
+
type: 'string',
|
|
2083
|
+
description: 'Text to search in',
|
|
2084
|
+
required: true,
|
|
2085
|
+
},
|
|
2086
|
+
{
|
|
2087
|
+
name: 'search',
|
|
2088
|
+
type: 'string',
|
|
2089
|
+
description: 'String to search for',
|
|
2090
|
+
required: true,
|
|
2091
|
+
},
|
|
2092
|
+
],
|
|
2093
|
+
returnType: 'boolean',
|
|
2094
|
+
examples: ['CONTAINS(@Name, "Project")'],
|
|
2095
|
+
},
|
|
2096
|
+
],
|
|
2097
|
+
},
|
|
2098
|
+
];
|
|
2099
|
+
const CONDITION_OPERATORS = [
|
|
2100
|
+
{ symbol: '==', name: 'Equal', type: 'comparison', description: 'Equal to', precedence: 3 },
|
|
2101
|
+
{ symbol: '!=', name: 'Not Equal', type: 'comparison', description: 'Not equal to', precedence: 3 },
|
|
2102
|
+
{ symbol: '>', name: 'Greater Than', type: 'comparison', description: 'Greater than', precedence: 4 },
|
|
2103
|
+
{ symbol: '<', name: 'Less Than', type: 'comparison', description: 'Less than', precedence: 4 },
|
|
2104
|
+
{ symbol: '>=', name: 'Greater or Equal', type: 'comparison', description: 'Greater than or equal', precedence: 4 },
|
|
2105
|
+
{ symbol: '<=', name: 'Less or Equal', type: 'comparison', description: 'Less than or equal', precedence: 4 },
|
|
2106
|
+
{ symbol: '&&', name: 'And', type: 'logical', description: 'Logical AND', precedence: 2 },
|
|
2107
|
+
{ symbol: '||', name: 'Or', type: 'logical', description: 'Logical OR', precedence: 1 },
|
|
2108
|
+
];
|
|
2109
|
+
const VALIDATION_FUNCTION_CATEGORIES = [
|
|
2110
|
+
{
|
|
2111
|
+
name: 'Validation',
|
|
2112
|
+
displayName: 'Validation',
|
|
2113
|
+
functions: [
|
|
2114
|
+
{
|
|
2115
|
+
name: 'REQUIRED',
|
|
2116
|
+
category: 'Validation',
|
|
2117
|
+
description: 'Returns true when value is not empty',
|
|
2118
|
+
signature: 'REQUIRED(value)',
|
|
2119
|
+
parameters: [
|
|
2120
|
+
{
|
|
2121
|
+
name: 'value',
|
|
2122
|
+
type: 'any',
|
|
2123
|
+
description: 'Value to check',
|
|
2124
|
+
required: true,
|
|
2125
|
+
},
|
|
2126
|
+
],
|
|
2127
|
+
returnType: 'boolean',
|
|
2128
|
+
examples: ['REQUIRED(@Email)'],
|
|
2129
|
+
},
|
|
2130
|
+
{
|
|
2131
|
+
name: 'ISNULL',
|
|
2132
|
+
category: 'Validation',
|
|
2133
|
+
description: 'Returns true if value is null or empty',
|
|
2134
|
+
signature: 'ISNULL(value)',
|
|
2135
|
+
parameters: [
|
|
2136
|
+
{
|
|
2137
|
+
name: 'value',
|
|
2138
|
+
type: 'any',
|
|
2139
|
+
description: 'Value to check',
|
|
2140
|
+
required: true,
|
|
2141
|
+
},
|
|
2142
|
+
],
|
|
2143
|
+
returnType: 'boolean',
|
|
2144
|
+
examples: ['ISNULL(@Phone)'],
|
|
2145
|
+
},
|
|
2146
|
+
{
|
|
2147
|
+
name: 'MIN_LENGTH',
|
|
2148
|
+
category: 'Validation',
|
|
2149
|
+
description: 'Returns true if value length is at least min',
|
|
2150
|
+
signature: 'MIN_LENGTH(value, min)',
|
|
2151
|
+
parameters: [
|
|
2152
|
+
{
|
|
2153
|
+
name: 'value',
|
|
2154
|
+
type: 'string',
|
|
2155
|
+
description: 'Value to check',
|
|
2156
|
+
required: true,
|
|
2157
|
+
},
|
|
2158
|
+
{
|
|
2159
|
+
name: 'min',
|
|
2160
|
+
type: 'number',
|
|
2161
|
+
description: 'Minimum length',
|
|
2162
|
+
required: true,
|
|
2163
|
+
},
|
|
2164
|
+
],
|
|
2165
|
+
returnType: 'boolean',
|
|
2166
|
+
examples: ['MIN_LENGTH(@Password, 8)'],
|
|
2167
|
+
},
|
|
2168
|
+
{
|
|
2169
|
+
name: 'MAX_LENGTH',
|
|
2170
|
+
category: 'Validation',
|
|
2171
|
+
description: 'Returns true if value length is at most max',
|
|
2172
|
+
signature: 'MAX_LENGTH(value, max)',
|
|
2173
|
+
parameters: [
|
|
2174
|
+
{
|
|
2175
|
+
name: 'value',
|
|
2176
|
+
type: 'string',
|
|
2177
|
+
description: 'Value to check',
|
|
2178
|
+
required: true,
|
|
2179
|
+
},
|
|
2180
|
+
{
|
|
2181
|
+
name: 'max',
|
|
2182
|
+
type: 'number',
|
|
2183
|
+
description: 'Maximum length',
|
|
2184
|
+
required: true,
|
|
2185
|
+
},
|
|
2186
|
+
],
|
|
2187
|
+
returnType: 'boolean',
|
|
2188
|
+
examples: ['MAX_LENGTH(@Title, 120)'],
|
|
2189
|
+
},
|
|
2190
|
+
{
|
|
2191
|
+
name: 'REGEX',
|
|
2192
|
+
category: 'Validation',
|
|
2193
|
+
description: 'Returns true if value matches regex pattern',
|
|
2194
|
+
signature: 'REGEX(value, pattern)',
|
|
2195
|
+
parameters: [
|
|
2196
|
+
{
|
|
2197
|
+
name: 'value',
|
|
2198
|
+
type: 'string',
|
|
2199
|
+
description: 'Value to check',
|
|
2200
|
+
required: true,
|
|
2201
|
+
},
|
|
2202
|
+
{
|
|
2203
|
+
name: 'pattern',
|
|
2204
|
+
type: 'string',
|
|
2205
|
+
description: 'Regex pattern',
|
|
2206
|
+
required: true,
|
|
2207
|
+
},
|
|
2208
|
+
],
|
|
2209
|
+
returnType: 'boolean',
|
|
2210
|
+
examples: ['REGEX(@Email, "^[^@]+@[^@]+\\.[^@]+$")'],
|
|
2211
|
+
},
|
|
2212
|
+
{
|
|
2213
|
+
name: 'BEFORE',
|
|
2214
|
+
category: 'Validation',
|
|
2215
|
+
description: 'Returns true if date is before reference date',
|
|
2216
|
+
signature: 'BEFORE(date, reference)',
|
|
2217
|
+
parameters: [
|
|
2218
|
+
{
|
|
2219
|
+
name: 'date',
|
|
2220
|
+
type: 'date',
|
|
2221
|
+
description: 'Date to check',
|
|
2222
|
+
required: true,
|
|
2223
|
+
},
|
|
2224
|
+
{
|
|
2225
|
+
name: 'reference',
|
|
2226
|
+
type: 'date',
|
|
2227
|
+
description: 'Reference date',
|
|
2228
|
+
required: true,
|
|
2229
|
+
},
|
|
2230
|
+
],
|
|
2231
|
+
returnType: 'boolean',
|
|
2232
|
+
examples: ['BEFORE(@EndDate, @StartDate)'],
|
|
2233
|
+
},
|
|
2234
|
+
{
|
|
2235
|
+
name: 'AFTER',
|
|
2236
|
+
category: 'Validation',
|
|
2237
|
+
description: 'Returns true if date is after reference date',
|
|
2238
|
+
signature: 'AFTER(date, reference)',
|
|
2239
|
+
parameters: [
|
|
2240
|
+
{
|
|
2241
|
+
name: 'date',
|
|
2242
|
+
type: 'date',
|
|
2243
|
+
description: 'Date to check',
|
|
2244
|
+
required: true,
|
|
2245
|
+
},
|
|
2246
|
+
{
|
|
2247
|
+
name: 'reference',
|
|
2248
|
+
type: 'date',
|
|
2249
|
+
description: 'Reference date',
|
|
2250
|
+
required: true,
|
|
2251
|
+
},
|
|
2252
|
+
],
|
|
2253
|
+
returnType: 'boolean',
|
|
2254
|
+
examples: ['AFTER(@StartDate, @EndDate)'],
|
|
2255
|
+
},
|
|
2256
|
+
],
|
|
2257
|
+
},
|
|
2258
|
+
{
|
|
2259
|
+
name: 'Logic',
|
|
2260
|
+
displayName: 'Logic Helpers',
|
|
2261
|
+
functions: [
|
|
2262
|
+
{
|
|
2263
|
+
name: 'AND',
|
|
2264
|
+
category: 'Logic',
|
|
2265
|
+
description: 'Returns true if all conditions are true',
|
|
2266
|
+
signature: 'AND(condition1, condition2)',
|
|
2267
|
+
parameters: [
|
|
2268
|
+
{
|
|
2269
|
+
name: 'condition1',
|
|
2270
|
+
type: 'boolean',
|
|
2271
|
+
description: 'First condition',
|
|
2272
|
+
required: true,
|
|
2273
|
+
},
|
|
2274
|
+
{
|
|
2275
|
+
name: 'condition2',
|
|
2276
|
+
type: 'boolean',
|
|
2277
|
+
description: 'Second condition',
|
|
2278
|
+
required: true,
|
|
2279
|
+
},
|
|
2280
|
+
],
|
|
2281
|
+
returnType: 'boolean',
|
|
2282
|
+
examples: ['AND(REQUIRED(@Email), REGEX(@Email, "^.+@.+$"))'],
|
|
2283
|
+
},
|
|
2284
|
+
{
|
|
2285
|
+
name: 'OR',
|
|
2286
|
+
category: 'Logic',
|
|
2287
|
+
description: 'Returns true if any condition is true',
|
|
2288
|
+
signature: 'OR(condition1, condition2)',
|
|
2289
|
+
parameters: [
|
|
2290
|
+
{
|
|
2291
|
+
name: 'condition1',
|
|
2292
|
+
type: 'boolean',
|
|
2293
|
+
description: 'First condition',
|
|
2294
|
+
required: true,
|
|
2295
|
+
},
|
|
2296
|
+
{
|
|
2297
|
+
name: 'condition2',
|
|
2298
|
+
type: 'boolean',
|
|
2299
|
+
description: 'Second condition',
|
|
2300
|
+
required: true,
|
|
2301
|
+
},
|
|
2302
|
+
],
|
|
2303
|
+
returnType: 'boolean',
|
|
2304
|
+
examples: ['OR(ISNULL(@Phone), REGEX(@Phone, "^\\+?[0-9]+$"))'],
|
|
2305
|
+
},
|
|
2306
|
+
{
|
|
2307
|
+
name: 'NOT',
|
|
2308
|
+
category: 'Logic',
|
|
2309
|
+
description: 'Returns the opposite of a condition',
|
|
2310
|
+
signature: 'NOT(condition)',
|
|
2311
|
+
parameters: [
|
|
2312
|
+
{
|
|
2313
|
+
name: 'condition',
|
|
2314
|
+
type: 'boolean',
|
|
2315
|
+
description: 'Condition to negate',
|
|
2316
|
+
required: true,
|
|
2317
|
+
},
|
|
2318
|
+
],
|
|
2319
|
+
returnType: 'boolean',
|
|
2320
|
+
examples: ['NOT(ISNULL(@Password))'],
|
|
2321
|
+
},
|
|
2322
|
+
],
|
|
2323
|
+
},
|
|
2324
|
+
];
|
|
2325
|
+
const VALIDATION_OPERATORS = [
|
|
2326
|
+
{ symbol: '+', name: 'Add', type: 'arithmetic', description: 'Addition', precedence: 4 },
|
|
2327
|
+
{ symbol: '-', name: 'Subtract', type: 'arithmetic', description: 'Subtraction', precedence: 4 },
|
|
2328
|
+
{ symbol: '*', name: 'Multiply', type: 'arithmetic', description: 'Multiplication', precedence: 5 },
|
|
2329
|
+
{ symbol: '/', name: 'Divide', type: 'arithmetic', description: 'Division', precedence: 5 },
|
|
2330
|
+
{ symbol: '==', name: 'Equal', type: 'comparison', description: 'Equal to', precedence: 3 },
|
|
2331
|
+
{ symbol: '!=', name: 'Not Equal', type: 'comparison', description: 'Not equal to', precedence: 3 },
|
|
2332
|
+
{ symbol: '>', name: 'Greater Than', type: 'comparison', description: 'Greater than', precedence: 3 },
|
|
2333
|
+
{ symbol: '<', name: 'Less Than', type: 'comparison', description: 'Less than', precedence: 3 },
|
|
2334
|
+
{ symbol: '>=', name: 'Greater or Equal', type: 'comparison', description: 'Greater than or equal', precedence: 3 },
|
|
2335
|
+
{ symbol: '<=', name: 'Less or Equal', type: 'comparison', description: 'Less than or equal', precedence: 3 },
|
|
2336
|
+
{ symbol: '&&', name: 'And', type: 'logical', description: 'Logical AND', precedence: 2 },
|
|
2337
|
+
{ symbol: '||', name: 'Or', type: 'logical', description: 'Logical OR', precedence: 1 },
|
|
2338
|
+
];
|
|
2339
|
+
const CONDITION_RUNTIME_CATALOG = {
|
|
2340
|
+
domain: 'condition',
|
|
2341
|
+
functions: CONDITION_FUNCTION_CATEGORIES,
|
|
2342
|
+
operators: CONDITION_OPERATORS,
|
|
2343
|
+
};
|
|
2344
|
+
const VALIDATION_RUNTIME_CATALOG = {
|
|
2345
|
+
domain: 'validation',
|
|
2346
|
+
functions: VALIDATION_FUNCTION_CATEGORIES,
|
|
2347
|
+
operators: VALIDATION_OPERATORS,
|
|
2348
|
+
};
|
|
2349
|
+
|
|
2350
|
+
const CONDITION_WRAPPER_NAMES = new Set([
|
|
2351
|
+
'show',
|
|
2352
|
+
'hide',
|
|
2353
|
+
'enable',
|
|
2354
|
+
'disable',
|
|
2355
|
+
]);
|
|
2356
|
+
const PROPERTY_PREFIXES = ['@', '*.', '$.'];
|
|
2357
|
+
class FormulaParser {
|
|
2358
|
+
tokens;
|
|
2359
|
+
current = 0;
|
|
2360
|
+
dependencies = new Set();
|
|
2361
|
+
constructor(tokens) {
|
|
2362
|
+
this.tokens = tokens;
|
|
2363
|
+
}
|
|
2364
|
+
parse() {
|
|
2365
|
+
const ast = this.expression();
|
|
2366
|
+
this.consume('eof', 'Unexpected tokens after formula end');
|
|
2367
|
+
const topLevelWrapper = ast.type === 'call' ? normalizeWrapperAction(ast.name) : null;
|
|
2368
|
+
return {
|
|
2369
|
+
ast,
|
|
2370
|
+
dependencies: Array.from(this.dependencies),
|
|
2371
|
+
topLevelWrapper,
|
|
2372
|
+
};
|
|
2373
|
+
}
|
|
2374
|
+
expression() {
|
|
2375
|
+
return this.logicalOr();
|
|
2376
|
+
}
|
|
2377
|
+
logicalOr() {
|
|
2378
|
+
let expr = this.logicalAnd();
|
|
2379
|
+
while (this.matchOperator('||')) {
|
|
2380
|
+
const operator = this.previousOperator();
|
|
2381
|
+
const right = this.logicalAnd();
|
|
2382
|
+
expr = { type: 'binary', operator, left: expr, right };
|
|
2383
|
+
}
|
|
2384
|
+
return expr;
|
|
2385
|
+
}
|
|
2386
|
+
logicalAnd() {
|
|
2387
|
+
let expr = this.equality();
|
|
2388
|
+
while (this.matchOperator('&&')) {
|
|
2389
|
+
const operator = this.previousOperator();
|
|
2390
|
+
const right = this.equality();
|
|
2391
|
+
expr = { type: 'binary', operator, left: expr, right };
|
|
2392
|
+
}
|
|
2393
|
+
return expr;
|
|
2394
|
+
}
|
|
2395
|
+
equality() {
|
|
2396
|
+
let expr = this.comparison();
|
|
2397
|
+
while (this.matchOperator('==', '!=')) {
|
|
2398
|
+
const operator = this.previousOperator();
|
|
2399
|
+
const right = this.comparison();
|
|
2400
|
+
expr = { type: 'binary', operator, left: expr, right };
|
|
2401
|
+
}
|
|
2402
|
+
return expr;
|
|
2403
|
+
}
|
|
2404
|
+
comparison() {
|
|
2405
|
+
let expr = this.addition();
|
|
2406
|
+
while (this.matchOperator('>', '>=', '<', '<=')) {
|
|
2407
|
+
const operator = this.previousOperator();
|
|
2408
|
+
const right = this.addition();
|
|
2409
|
+
expr = { type: 'binary', operator, left: expr, right };
|
|
2410
|
+
}
|
|
2411
|
+
return expr;
|
|
2412
|
+
}
|
|
2413
|
+
addition() {
|
|
2414
|
+
let expr = this.multiplication();
|
|
2415
|
+
while (this.matchOperator('+', '-')) {
|
|
2416
|
+
const operator = this.previousOperator();
|
|
2417
|
+
const right = this.multiplication();
|
|
2418
|
+
expr = { type: 'binary', operator, left: expr, right };
|
|
2419
|
+
}
|
|
2420
|
+
return expr;
|
|
2421
|
+
}
|
|
2422
|
+
multiplication() {
|
|
2423
|
+
let expr = this.unary();
|
|
2424
|
+
while (this.matchOperator('*', '/')) {
|
|
2425
|
+
const operator = this.previousOperator();
|
|
2426
|
+
const right = this.unary();
|
|
2427
|
+
expr = { type: 'binary', operator, left: expr, right };
|
|
2428
|
+
}
|
|
2429
|
+
return expr;
|
|
2430
|
+
}
|
|
2431
|
+
unary() {
|
|
2432
|
+
if (this.matchOperator('!', '-')) {
|
|
2433
|
+
const operator = this.previousOperator();
|
|
2434
|
+
const right = this.unary();
|
|
2435
|
+
return { type: 'unary', operator, right };
|
|
2436
|
+
}
|
|
2437
|
+
return this.primary();
|
|
2438
|
+
}
|
|
2439
|
+
primary() {
|
|
2440
|
+
if (this.match('literal')) {
|
|
2441
|
+
return { type: 'literal', value: this.previous().value };
|
|
2442
|
+
}
|
|
2443
|
+
if (this.match('property')) {
|
|
2444
|
+
const token = this.previousProperty();
|
|
2445
|
+
this.dependencies.add(token.value);
|
|
2446
|
+
return { type: 'property', key: token.value, raw: token.raw };
|
|
2447
|
+
}
|
|
2448
|
+
if (this.match('identifier')) {
|
|
2449
|
+
const name = this.previous().value;
|
|
2450
|
+
if (!this.match('lparen')) {
|
|
2451
|
+
throw new Error(`Expected "(" after function "${name}"`);
|
|
2452
|
+
}
|
|
2453
|
+
const args = [];
|
|
2454
|
+
if (!this.check('rparen')) {
|
|
2455
|
+
do {
|
|
2456
|
+
args.push(this.expression());
|
|
2457
|
+
} while (this.match('comma'));
|
|
2458
|
+
}
|
|
2459
|
+
this.consume('rparen', `Expected ")" after function "${name}"`);
|
|
2460
|
+
return { type: 'call', name, args };
|
|
2461
|
+
}
|
|
2462
|
+
if (this.match('lparen')) {
|
|
2463
|
+
const expr = this.expression();
|
|
2464
|
+
this.consume('rparen', 'Expected ")" after expression');
|
|
2465
|
+
return expr;
|
|
2466
|
+
}
|
|
2467
|
+
throw new Error('Unexpected token while parsing formula');
|
|
2468
|
+
}
|
|
2469
|
+
previous() {
|
|
2470
|
+
return this.tokens[this.current - 1];
|
|
2471
|
+
}
|
|
2472
|
+
previousProperty() {
|
|
2473
|
+
return this.tokens[this.current - 1];
|
|
2474
|
+
}
|
|
2475
|
+
previousOperator() {
|
|
2476
|
+
return this.tokens[this.current - 1].value;
|
|
2477
|
+
}
|
|
2478
|
+
consume(type, message) {
|
|
2479
|
+
if (this.check(type)) {
|
|
2480
|
+
this.advance();
|
|
2481
|
+
return;
|
|
2482
|
+
}
|
|
2483
|
+
throw new Error(message);
|
|
2484
|
+
}
|
|
2485
|
+
match(type) {
|
|
2486
|
+
if (!this.check(type))
|
|
2487
|
+
return false;
|
|
2488
|
+
this.advance();
|
|
2489
|
+
return true;
|
|
2490
|
+
}
|
|
2491
|
+
matchOperator(...operators) {
|
|
2492
|
+
if (!this.check('operator'))
|
|
2493
|
+
return false;
|
|
2494
|
+
const token = this.peek();
|
|
2495
|
+
if (!operators.includes(token.value))
|
|
2496
|
+
return false;
|
|
2497
|
+
this.advance();
|
|
2498
|
+
return true;
|
|
2499
|
+
}
|
|
2500
|
+
check(type) {
|
|
2501
|
+
if (this.isAtEnd())
|
|
2502
|
+
return type === 'eof';
|
|
2503
|
+
return this.peek().type === type;
|
|
2504
|
+
}
|
|
2505
|
+
advance() {
|
|
2506
|
+
if (!this.isAtEnd())
|
|
2507
|
+
this.current++;
|
|
2508
|
+
return this.tokens[this.current - 1];
|
|
2509
|
+
}
|
|
2510
|
+
isAtEnd() {
|
|
2511
|
+
return this.peek().type === 'eof';
|
|
2512
|
+
}
|
|
2513
|
+
peek() {
|
|
2514
|
+
return this.tokens[this.current];
|
|
2515
|
+
}
|
|
2516
|
+
}
|
|
2517
|
+
class FormulaRuntimeEngine {
|
|
2518
|
+
compiledCache = new Map();
|
|
2519
|
+
functionRegistry;
|
|
2520
|
+
constructor(customFunctions) {
|
|
2521
|
+
this.functionRegistry = {
|
|
2522
|
+
...buildDefaultFunctionRegistry(),
|
|
2523
|
+
...(customFunctions ?? {}),
|
|
2524
|
+
};
|
|
2525
|
+
}
|
|
2526
|
+
evaluateCondition(config, context) {
|
|
2527
|
+
try {
|
|
2528
|
+
const compiled = this.getCompiledFormula(config.formulaTokens);
|
|
2529
|
+
const rawValue = this.evaluateAst(compiled.ast, context.values);
|
|
2530
|
+
const action = this.resolveConditionAction(config.mode, compiled);
|
|
2531
|
+
const conditionValue = toBoolean(rawValue);
|
|
2532
|
+
switch (action) {
|
|
2533
|
+
case 'show':
|
|
2534
|
+
return {
|
|
2535
|
+
hidden: !conditionValue,
|
|
2536
|
+
dependencies: compiled.dependencies,
|
|
2537
|
+
errors: [],
|
|
2538
|
+
};
|
|
2539
|
+
case 'hide':
|
|
2540
|
+
return {
|
|
2541
|
+
hidden: conditionValue,
|
|
2542
|
+
dependencies: compiled.dependencies,
|
|
2543
|
+
errors: [],
|
|
2544
|
+
};
|
|
2545
|
+
case 'enable':
|
|
2546
|
+
return {
|
|
2547
|
+
disabled: !conditionValue,
|
|
2548
|
+
dependencies: compiled.dependencies,
|
|
2549
|
+
errors: [],
|
|
2550
|
+
};
|
|
2551
|
+
case 'disable':
|
|
2552
|
+
return {
|
|
2553
|
+
disabled: conditionValue,
|
|
2554
|
+
dependencies: compiled.dependencies,
|
|
2555
|
+
errors: [],
|
|
2556
|
+
};
|
|
2557
|
+
default:
|
|
2558
|
+
return {
|
|
2559
|
+
dependencies: compiled.dependencies,
|
|
2560
|
+
errors: [],
|
|
2561
|
+
};
|
|
2562
|
+
}
|
|
2563
|
+
}
|
|
2564
|
+
catch (error) {
|
|
2565
|
+
return {
|
|
2566
|
+
dependencies: [],
|
|
2567
|
+
errors: [toErrorMessage(error)],
|
|
2568
|
+
};
|
|
2569
|
+
}
|
|
2570
|
+
}
|
|
2571
|
+
evaluateValidation(rule, context) {
|
|
2572
|
+
try {
|
|
2573
|
+
const compiled = this.getCompiledFormula(rule.formulaTokens);
|
|
2574
|
+
const value = this.evaluateAst(compiled.ast, context.values);
|
|
2575
|
+
return {
|
|
2576
|
+
valid: toBoolean(value),
|
|
2577
|
+
dependencies: compiled.dependencies,
|
|
2578
|
+
errors: [],
|
|
2579
|
+
};
|
|
2580
|
+
}
|
|
2581
|
+
catch (error) {
|
|
2582
|
+
return {
|
|
2583
|
+
valid: false,
|
|
2584
|
+
dependencies: [],
|
|
2585
|
+
errors: [toErrorMessage(error)],
|
|
2586
|
+
};
|
|
2587
|
+
}
|
|
2588
|
+
}
|
|
2589
|
+
extractDependencies(formulaTokens) {
|
|
2590
|
+
try {
|
|
2591
|
+
return this.getCompiledFormula(formulaTokens).dependencies;
|
|
2592
|
+
}
|
|
2593
|
+
catch {
|
|
2594
|
+
return [];
|
|
2595
|
+
}
|
|
2596
|
+
}
|
|
2597
|
+
resolveConditionAction(mode, compiled) {
|
|
2598
|
+
if (mode && mode !== 'auto')
|
|
2599
|
+
return mode;
|
|
2600
|
+
return compiled.topLevelWrapper ?? 'show';
|
|
2601
|
+
}
|
|
2602
|
+
getCompiledFormula(formulaTokens) {
|
|
2603
|
+
const key = formulaTokens?.trim();
|
|
2604
|
+
if (!key) {
|
|
2605
|
+
throw new Error('Formula tokens are empty');
|
|
2606
|
+
}
|
|
2607
|
+
const cached = this.compiledCache.get(key);
|
|
2608
|
+
if (cached)
|
|
2609
|
+
return cached;
|
|
2610
|
+
const tokens = this.deserializeFormulaTokens(key);
|
|
2611
|
+
const lexTokens = this.toLexTokens(tokens);
|
|
2612
|
+
const compiled = new FormulaParser(lexTokens).parse();
|
|
2613
|
+
this.compiledCache.set(key, compiled);
|
|
2614
|
+
return compiled;
|
|
2615
|
+
}
|
|
2616
|
+
deserializeFormulaTokens(formulaTokens) {
|
|
2617
|
+
let parsed;
|
|
2618
|
+
try {
|
|
2619
|
+
parsed = JSON.parse(formulaTokens);
|
|
2620
|
+
}
|
|
2621
|
+
catch {
|
|
2622
|
+
throw new Error('Formula tokens JSON is invalid');
|
|
2623
|
+
}
|
|
2624
|
+
if (!Array.isArray(parsed)) {
|
|
2625
|
+
throw new Error('Formula tokens must be an array');
|
|
2626
|
+
}
|
|
2627
|
+
return parsed;
|
|
2628
|
+
}
|
|
2629
|
+
toLexTokens(tokens) {
|
|
2630
|
+
if (!tokens.length) {
|
|
2631
|
+
throw new Error('Formula token array is empty');
|
|
2632
|
+
}
|
|
2633
|
+
const lex = [];
|
|
2634
|
+
for (const token of tokens) {
|
|
2635
|
+
switch (token.type) {
|
|
2636
|
+
case 'fn-open': {
|
|
2637
|
+
const functionName = (token.functionName ?? token.value.replace(/\($/, '')).trim();
|
|
2638
|
+
if (!functionName) {
|
|
2639
|
+
throw new Error('Formula contains function token without a name');
|
|
2640
|
+
}
|
|
2641
|
+
lex.push({ type: 'identifier', value: functionName });
|
|
2642
|
+
lex.push({ type: 'lparen' });
|
|
2643
|
+
break;
|
|
2644
|
+
}
|
|
2645
|
+
case 'fn-close':
|
|
2646
|
+
lex.push({ type: 'rparen' });
|
|
2647
|
+
break;
|
|
2648
|
+
case 'fn-separator':
|
|
2649
|
+
lex.push({ type: 'comma' });
|
|
2650
|
+
break;
|
|
2651
|
+
case 'operator':
|
|
2652
|
+
lex.push({ type: 'operator', value: token.value });
|
|
2653
|
+
break;
|
|
2654
|
+
case 'property':
|
|
2655
|
+
lex.push({
|
|
2656
|
+
type: 'property',
|
|
2657
|
+
value: normalizePropertyToken(token.value),
|
|
2658
|
+
raw: token.value,
|
|
2659
|
+
});
|
|
2660
|
+
break;
|
|
2661
|
+
case 'literal':
|
|
2662
|
+
lex.push({ type: 'literal', value: parseLiteralTokenValue(token) });
|
|
2663
|
+
break;
|
|
2664
|
+
default:
|
|
2665
|
+
throw new Error(`Unsupported formula token type: ${token.type}`);
|
|
2666
|
+
}
|
|
2667
|
+
}
|
|
2668
|
+
lex.push({ type: 'eof' });
|
|
2669
|
+
return lex;
|
|
2670
|
+
}
|
|
2671
|
+
evaluateAst(ast, values) {
|
|
2672
|
+
switch (ast.type) {
|
|
2673
|
+
case 'literal':
|
|
2674
|
+
return ast.value;
|
|
2675
|
+
case 'property':
|
|
2676
|
+
return resolvePropertyValue(values, ast.key, ast.raw);
|
|
2677
|
+
case 'unary': {
|
|
2678
|
+
const right = this.evaluateAst(ast.right, values);
|
|
2679
|
+
if (ast.operator === '!')
|
|
2680
|
+
return !toBoolean(right);
|
|
2681
|
+
if (ast.operator === '-')
|
|
2682
|
+
return -toNumber(right);
|
|
2683
|
+
throw new Error(`Unsupported unary operator: ${ast.operator}`);
|
|
2684
|
+
}
|
|
2685
|
+
case 'binary': {
|
|
2686
|
+
const left = this.evaluateAst(ast.left, values);
|
|
2687
|
+
const right = this.evaluateAst(ast.right, values);
|
|
2688
|
+
return evaluateBinary(ast.operator, left, right);
|
|
2689
|
+
}
|
|
2690
|
+
case 'call': {
|
|
2691
|
+
const fn = this.functionRegistry[ast.name.toUpperCase()];
|
|
2692
|
+
if (!fn) {
|
|
2693
|
+
throw new Error(`Unsupported function: ${ast.name}`);
|
|
2694
|
+
}
|
|
2695
|
+
const args = ast.args.map((arg) => this.evaluateAst(arg, values));
|
|
2696
|
+
return fn(...args);
|
|
2697
|
+
}
|
|
2698
|
+
default:
|
|
2699
|
+
throw new Error('Unsupported formula AST node');
|
|
2700
|
+
}
|
|
2701
|
+
}
|
|
2702
|
+
}
|
|
2703
|
+
function evaluateBinary(operator, left, right) {
|
|
2704
|
+
switch (operator) {
|
|
2705
|
+
case '||':
|
|
2706
|
+
return toBoolean(left) || toBoolean(right);
|
|
2707
|
+
case '&&':
|
|
2708
|
+
return toBoolean(left) && toBoolean(right);
|
|
2709
|
+
case '==':
|
|
2710
|
+
return looseEqual(left, right);
|
|
2711
|
+
case '!=':
|
|
2712
|
+
return !looseEqual(left, right);
|
|
2713
|
+
case '>':
|
|
2714
|
+
return compareValues(left, right) > 0;
|
|
2715
|
+
case '>=':
|
|
2716
|
+
return compareValues(left, right) >= 0;
|
|
2717
|
+
case '<':
|
|
2718
|
+
return compareValues(left, right) < 0;
|
|
2719
|
+
case '<=':
|
|
2720
|
+
return compareValues(left, right) <= 0;
|
|
2721
|
+
case '+':
|
|
2722
|
+
return typeof left === 'string' || typeof right === 'string'
|
|
2723
|
+
? `${left ?? ''}${right ?? ''}`
|
|
2724
|
+
: toNumber(left) + toNumber(right);
|
|
2725
|
+
case '-':
|
|
2726
|
+
return toNumber(left) - toNumber(right);
|
|
2727
|
+
case '*':
|
|
2728
|
+
return toNumber(left) * toNumber(right);
|
|
2729
|
+
case '/':
|
|
2730
|
+
return toNumber(left) / toNumber(right);
|
|
2731
|
+
default:
|
|
2732
|
+
throw new Error(`Unsupported operator: ${operator}`);
|
|
2733
|
+
}
|
|
2734
|
+
}
|
|
2735
|
+
function looseEqual(left, right) {
|
|
2736
|
+
if (Array.isArray(left) && Array.isArray(right)) {
|
|
2737
|
+
return right.every((item) => left.includes(item));
|
|
2738
|
+
}
|
|
2739
|
+
if (Array.isArray(left))
|
|
2740
|
+
return left.includes(right);
|
|
2741
|
+
if (Array.isArray(right))
|
|
2742
|
+
return right.includes(left);
|
|
2743
|
+
const leftDate = toDate(left);
|
|
2744
|
+
const rightDate = toDate(right);
|
|
2745
|
+
if (leftDate && rightDate) {
|
|
2746
|
+
return leftDate.getTime() === rightDate.getTime();
|
|
2747
|
+
}
|
|
2748
|
+
const leftNumber = maybeNumber(left);
|
|
2749
|
+
const rightNumber = maybeNumber(right);
|
|
2750
|
+
if (leftNumber !== null && rightNumber !== null) {
|
|
2751
|
+
return leftNumber === rightNumber;
|
|
2752
|
+
}
|
|
2753
|
+
return left === right;
|
|
2754
|
+
}
|
|
2755
|
+
function compareValues(left, right) {
|
|
2756
|
+
const leftDate = toDate(left);
|
|
2757
|
+
const rightDate = toDate(right);
|
|
2758
|
+
if (leftDate && rightDate) {
|
|
2759
|
+
return leftDate.getTime() - rightDate.getTime();
|
|
2760
|
+
}
|
|
2761
|
+
const leftNumber = toNumber(left);
|
|
2762
|
+
const rightNumber = toNumber(right);
|
|
2763
|
+
return leftNumber - rightNumber;
|
|
2764
|
+
}
|
|
2765
|
+
function buildDefaultFunctionRegistry() {
|
|
2766
|
+
const isNull = (value) => value === null ||
|
|
2767
|
+
value === undefined ||
|
|
2768
|
+
value === '' ||
|
|
2769
|
+
(Array.isArray(value) && value.length === 0);
|
|
2770
|
+
return {
|
|
2771
|
+
SHOW_IF: (condition) => toBoolean(condition),
|
|
2772
|
+
HIDE_IF: (condition) => toBoolean(condition),
|
|
2773
|
+
ENABLE_IF: (condition) => toBoolean(condition),
|
|
2774
|
+
DISABLE_IF: (condition) => toBoolean(condition),
|
|
2775
|
+
AND: (...args) => args.every((arg) => toBoolean(arg)),
|
|
2776
|
+
OR: (...args) => args.some((arg) => toBoolean(arg)),
|
|
2777
|
+
NOT: (condition) => !toBoolean(condition),
|
|
2778
|
+
ISNULL: (value) => isNull(value),
|
|
2779
|
+
CONTAINS: (text, search) => String(text ?? '')
|
|
2780
|
+
.toLowerCase()
|
|
2781
|
+
.includes(String(search ?? '').toLowerCase()),
|
|
2782
|
+
REQUIRED: (value) => !isNull(value),
|
|
2783
|
+
MIN_LENGTH: (value, min) => String(value ?? '').length >= toNumber(min),
|
|
2784
|
+
MAX_LENGTH: (value, max) => String(value ?? '').length <= toNumber(max),
|
|
2785
|
+
REGEX: (value, pattern) => {
|
|
2786
|
+
try {
|
|
2787
|
+
return new RegExp(String(pattern ?? '')).test(String(value ?? ''));
|
|
2788
|
+
}
|
|
2789
|
+
catch {
|
|
2790
|
+
return false;
|
|
2791
|
+
}
|
|
2792
|
+
},
|
|
2793
|
+
BEFORE: (date, reference) => {
|
|
2794
|
+
const left = toDate(date);
|
|
2795
|
+
const right = toDate(reference);
|
|
2796
|
+
if (!left || !right)
|
|
2797
|
+
return false;
|
|
2798
|
+
return left.getTime() < right.getTime();
|
|
2799
|
+
},
|
|
2800
|
+
AFTER: (date, reference) => {
|
|
2801
|
+
const left = toDate(date);
|
|
2802
|
+
const right = toDate(reference);
|
|
2803
|
+
if (!left || !right)
|
|
2804
|
+
return false;
|
|
2805
|
+
return left.getTime() > right.getTime();
|
|
2806
|
+
},
|
|
2807
|
+
};
|
|
2808
|
+
}
|
|
2809
|
+
function normalizeWrapperAction(name) {
|
|
2810
|
+
const normalized = name.trim().toLowerCase();
|
|
2811
|
+
return CONDITION_WRAPPER_NAMES.has(normalized)
|
|
2812
|
+
? normalized
|
|
2813
|
+
: null;
|
|
2814
|
+
}
|
|
2815
|
+
function normalizePropertyToken(raw) {
|
|
2816
|
+
const value = String(raw ?? '').trim();
|
|
2817
|
+
const prefix = PROPERTY_PREFIXES.find((p) => value.startsWith(p));
|
|
2818
|
+
return prefix ? value.slice(prefix.length) : value;
|
|
2819
|
+
}
|
|
2820
|
+
function parseLiteralTokenValue(token) {
|
|
2821
|
+
if (token.literalType === 'number') {
|
|
2822
|
+
return toNumber(token.value);
|
|
2823
|
+
}
|
|
2824
|
+
if (token.literalType === 'boolean') {
|
|
2825
|
+
return toBoolean(token.value);
|
|
2826
|
+
}
|
|
2827
|
+
const value = String(token.value ?? '').trim();
|
|
2828
|
+
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
2829
|
+
(value.startsWith("'") && value.endsWith("'"))) {
|
|
2830
|
+
return value.slice(1, -1);
|
|
2831
|
+
}
|
|
2832
|
+
if (value.toLowerCase() === 'true')
|
|
2833
|
+
return true;
|
|
2834
|
+
if (value.toLowerCase() === 'false')
|
|
2835
|
+
return false;
|
|
2836
|
+
const asNumber = maybeNumber(value);
|
|
2837
|
+
if (asNumber !== null)
|
|
2838
|
+
return asNumber;
|
|
2839
|
+
return value;
|
|
2840
|
+
}
|
|
2841
|
+
function resolvePropertyValue(values, key, raw) {
|
|
2842
|
+
if (Object.prototype.hasOwnProperty.call(values, key)) {
|
|
2843
|
+
return values[key];
|
|
2844
|
+
}
|
|
2845
|
+
if (Object.prototype.hasOwnProperty.call(values, raw)) {
|
|
2846
|
+
return values[raw];
|
|
2847
|
+
}
|
|
2848
|
+
if (!key.includes('.')) {
|
|
2849
|
+
return undefined;
|
|
2850
|
+
}
|
|
2851
|
+
const parts = key.split('.');
|
|
2852
|
+
let current = values;
|
|
2853
|
+
for (const part of parts) {
|
|
2854
|
+
if (current === null || current === undefined) {
|
|
2855
|
+
return undefined;
|
|
2856
|
+
}
|
|
2857
|
+
current = current[part];
|
|
2858
|
+
}
|
|
2859
|
+
return current;
|
|
2860
|
+
}
|
|
2861
|
+
function toBoolean(value) {
|
|
2862
|
+
if (typeof value === 'boolean')
|
|
2863
|
+
return value;
|
|
2864
|
+
if (typeof value === 'number')
|
|
2865
|
+
return !Number.isNaN(value) && value !== 0;
|
|
2866
|
+
if (typeof value === 'string') {
|
|
2867
|
+
const normalized = value.trim().toLowerCase();
|
|
2868
|
+
if (normalized === '' ||
|
|
2869
|
+
normalized === 'false' ||
|
|
2870
|
+
normalized === '0' ||
|
|
2871
|
+
normalized === 'null' ||
|
|
2872
|
+
normalized === 'undefined') {
|
|
2873
|
+
return false;
|
|
2874
|
+
}
|
|
2875
|
+
return true;
|
|
2876
|
+
}
|
|
2877
|
+
if (Array.isArray(value))
|
|
2878
|
+
return value.length > 0;
|
|
2879
|
+
return Boolean(value);
|
|
2880
|
+
}
|
|
2881
|
+
function toNumber(value) {
|
|
2882
|
+
const num = Number(value);
|
|
2883
|
+
return Number.isFinite(num) ? num : 0;
|
|
2884
|
+
}
|
|
2885
|
+
function maybeNumber(value) {
|
|
2886
|
+
if (value === '' || value === null || value === undefined)
|
|
2887
|
+
return null;
|
|
2888
|
+
const num = Number(value);
|
|
2889
|
+
return Number.isFinite(num) ? num : null;
|
|
2890
|
+
}
|
|
2891
|
+
function toDate(value) {
|
|
2892
|
+
if (value instanceof Date) {
|
|
2893
|
+
return Number.isNaN(value.getTime()) ? null : value;
|
|
2894
|
+
}
|
|
2895
|
+
if (typeof value === 'string' || typeof value === 'number') {
|
|
2896
|
+
const date = new Date(value);
|
|
2897
|
+
return Number.isNaN(date.getTime()) ? null : date;
|
|
2898
|
+
}
|
|
2899
|
+
return null;
|
|
2900
|
+
}
|
|
2901
|
+
function toErrorMessage(error) {
|
|
2902
|
+
if (error instanceof Error && error.message)
|
|
2903
|
+
return error.message;
|
|
2904
|
+
return 'Formula evaluation failed';
|
|
2905
|
+
}
|
|
2906
|
+
|
|
1923
2907
|
// Formula components public API
|
|
1924
2908
|
|
|
1925
2909
|
/**
|
|
1926
2910
|
* Generated bundle index. Do not edit.
|
|
1927
2911
|
*/
|
|
1928
2912
|
|
|
1929
|
-
export { DEFAULT_OPERATORS, FormulaEditor, FormulaEditorCode, FormulaStatusBar, FormulaToolbar, FormulaToolbarItem, cloneBlock, cloneToken, cloneTokens, createFunctionBlock, createFunctionTokens, createLiteralBlock, createLiteralToken, createOperatorBlock, createOperatorToken, createPropertyBlock, createPropertyToken, findFunctionRange, generateFunctionId, generateSmartBlockId, generateTokenId, getArgumentIndexAtPosition, getFunctionTokens, isValidDropPosition, parseSignature, recalculateDepths, serializeTokens };
|
|
2913
|
+
export { CONDITION_FUNCTION_CATEGORIES, CONDITION_OPERATORS, CONDITION_RUNTIME_CATALOG, DEFAULT_OPERATORS, FormulaEditor, FormulaEditorCode, FormulaRuntimeEngine, FormulaStatusBar, FormulaToolbar, FormulaToolbarItem, VALIDATION_FUNCTION_CATEGORIES, VALIDATION_OPERATORS, VALIDATION_RUNTIME_CATALOG, cloneBlock, cloneToken, cloneTokens, createFunctionBlock, createFunctionTokens, createLiteralBlock, createLiteralToken, createOperatorBlock, createOperatorToken, createPropertyBlock, createPropertyToken, findFunctionRange, generateFunctionId, generateSmartBlockId, generateTokenId, getArgumentIndexAtPosition, getFunctionTokens, isValidDropPosition, parseSignature, recalculateDepths, serializeTokens };
|
|
1930
2914
|
//# sourceMappingURL=masterteam-components-formula.mjs.map
|