@evoke-platform/ui-components 1.0.0-dev.239 → 1.0.0-dev.241

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.
@@ -9,22 +9,22 @@ import { Box } from '../../layout';
9
9
  import { difference } from '../util';
10
10
  import ValueEditor from './ValueEditor';
11
11
  const ALL_OPERATORS = [
12
- { name: '=', label: '=' },
13
- { name: '!=', label: '!=' },
14
- { name: '<', label: '<' },
15
- { name: '>', label: '>' },
16
- { name: '<=', label: '<=' },
17
- { name: '>=', label: '>=' },
18
- { name: 'contains', label: 'contains' },
19
- { name: 'beginsWith', label: 'begins with' },
20
- { name: 'endsWith', label: 'ends with' },
21
- { name: 'doesNotContain', label: 'does not contain' },
22
- { name: 'doesNotBeginWith', label: 'does not begin with' },
23
- { name: 'doesNotEndWith', label: 'does not end with' },
24
- { name: 'null', label: 'is null' },
25
- { name: 'notNull', label: 'is not null' },
26
- { name: 'in', label: 'in' },
27
- { name: 'notIn', label: 'not in' },
12
+ { name: '=', label: 'Is' },
13
+ { name: '!=', label: 'Is not' },
14
+ { name: '<', label: 'Less than' },
15
+ { name: '>', label: 'Greater than' },
16
+ { name: '<=', label: 'Less than or equal to' },
17
+ { name: '>=', label: 'Greater than or equal to' },
18
+ { name: 'contains', label: 'Contains' },
19
+ { name: 'beginsWith', label: 'Starts with' },
20
+ { name: 'endsWith', label: 'Ends with' },
21
+ { name: 'doesNotContain', label: 'Does not contain' },
22
+ { name: 'doesNotBeginWith', label: 'Does not start with' },
23
+ { name: 'doesNotEndWith', label: 'Does not end with' },
24
+ { name: 'null', label: 'Is empty' },
25
+ { name: 'notNull', label: 'Is not empty' },
26
+ { name: 'in', label: 'In' },
27
+ { name: 'notIn', label: 'Not in' },
28
28
  ];
29
29
  const CustomRuleGroup = (props) => {
30
30
  const rg = { ...props, ...useRuleGroup(props) };
@@ -86,6 +86,7 @@ const customSelector = (props) => {
86
86
  let width = '90px';
87
87
  let val = value;
88
88
  let opts = options;
89
+ const inputType = props.fieldData?.inputType;
89
90
  let readOnly = false;
90
91
  if (context.disabledCriteria) {
91
92
  readOnly =
@@ -98,18 +99,42 @@ const customSelector = (props) => {
98
99
  switch (title) {
99
100
  case 'Operators':
100
101
  width = '20%';
101
- if (props.fieldData?.inputType === 'array') {
102
+ if (inputType === 'array') {
102
103
  opts = options
103
104
  .filter((option) => ['null', 'notNull', 'in', 'notIn'].includes(option.name))
104
105
  .map((option) => ({ name: option.name, label: option.label }));
105
106
  val = val === '=' ? '' : options.find((option) => option.name === val).name;
106
107
  }
107
- else if (props.fieldData?.inputType === 'document') {
108
+ else if (inputType === 'document') {
108
109
  opts = options
109
110
  .filter((option) => ['null', 'notNull'].includes(option.name))
110
111
  .map((option) => ({ name: option.name, label: option.label }));
111
112
  val = val === '=' ? '' : options.find((option) => option.name === val).name;
112
113
  }
114
+ else if (inputType === 'date' || inputType === 'date-time' || inputType === 'time') {
115
+ opts = options
116
+ .filter((option) => ['=', '!=', '<', '>', '<=', '>=', 'null', 'notNull'].includes(option.name))
117
+ .map((option) => ({
118
+ ...option,
119
+ label: option.name === '>'
120
+ ? inputType === 'time'
121
+ ? 'Later than'
122
+ : 'After'
123
+ : option.name === '>='
124
+ ? inputType === 'time'
125
+ ? 'Later than or at'
126
+ : 'After or on'
127
+ : option.name === '<'
128
+ ? inputType === 'time'
129
+ ? 'Earlier than'
130
+ : 'Before'
131
+ : option.name === '<='
132
+ ? inputType === 'time'
133
+ ? 'Earlier than or at'
134
+ : 'Before or on'
135
+ : option.label,
136
+ }));
137
+ }
113
138
  break;
114
139
  case 'Fields':
115
140
  width = '33%';
@@ -1,5 +1,4 @@
1
1
  import { ReactComponent } from '@formio/react';
2
- import Handlebars from 'handlebars';
3
2
  import { isArray, isEmpty, isNil, uniq } from 'lodash';
4
3
  import { DateTime } from 'luxon';
5
4
  import React from 'react';
@@ -10,20 +9,20 @@ import { isPropertyVisible } from '../utils';
10
9
  function isAddressProperties(key) {
11
10
  return /\.line1|\.line2|\.city|\.county|\.state|\.zipCode$/.test(key);
12
11
  }
13
- Handlebars.registerHelper('addDays', function (addend1, addend2) {
12
+ function addDays(addend1, addend2) {
14
13
  const dateAddend1 = DateTime.fromISO(addend1);
15
14
  if (dateAddend1.isValid) {
16
15
  return dateAddend1.plus({ days: addend2 }).toISODate();
17
16
  }
18
17
  return undefined;
19
- });
20
- Handlebars.registerHelper('subDays', function (minuend, subtrahend) {
18
+ }
19
+ function subDays(minuend, subtrahend) {
21
20
  const dateMinuend = DateTime.fromISO(minuend);
22
21
  if (dateMinuend.isValid) {
23
22
  return dateMinuend.minus({ days: subtrahend }).toISODate();
24
23
  }
25
24
  return undefined;
26
- });
25
+ }
27
26
  export class FormFieldComponent extends ReactComponent {
28
27
  /**
29
28
  * Called when the component has been instantiated. This is useful to define
@@ -345,29 +344,63 @@ export class FormFieldComponent extends ReactComponent {
345
344
  else {
346
345
  delete this.errorDetails['regexes'];
347
346
  }
348
- if (this.component.validate.minDate || this.component.validate.maxDate) {
347
+ const evaluateDateValidation = (dateValidation) => {
349
348
  const dateRegex = /^\d{4}-\d{2}-\d{2}$/;
350
- const data = {
351
- input: { ...this.root.data },
352
- __today__: DateTime.now().toISODate(),
353
- };
354
- let { minDate, maxDate } = validate;
355
- if (minDate && !dateRegex.test(minDate)) {
356
- try {
357
- minDate = Handlebars.compile(minDate)(data);
358
- }
359
- catch (err) {
360
- console.log(err);
361
- }
362
- }
363
- if (maxDate && !dateRegex.test(maxDate)) {
349
+ const exactDateRegex = /^{{(?<inputProperty>input\.[a-zA-Z][a-zA-Z0-9_]*|__today__)}}$/;
350
+ const relativeDateRegex = /^{{(?<operation>addDays|subDays) (?<operand1>input\.[a-zA-Z][a-zA-Z0-9_]*|__today__) (?<operand2>[0-9]+)}}$/;
351
+ // If the date validation isn't a literal date, evaluate it
352
+ if (dateValidation && !dateRegex.test(dateValidation)) {
364
353
  try {
365
- maxDate = Handlebars.compile(maxDate)(data);
354
+ const today = DateTime.now().toISODate();
355
+ if (exactDateRegex.test(dateValidation)) {
356
+ const { groups = {} } = exactDateRegex.exec(dateValidation) ?? {};
357
+ const { inputProperty } = groups;
358
+ if (inputProperty === '__today__') {
359
+ return today;
360
+ }
361
+ else if (inputProperty.split('.').length > 1) {
362
+ const propertyId = inputProperty.split('.')[1];
363
+ if (propertyId in this.root.data) {
364
+ return this.root.data[propertyId];
365
+ }
366
+ }
367
+ }
368
+ else if (relativeDateRegex.test(dateValidation)) {
369
+ const { groups = {} } = relativeDateRegex.exec(dateValidation) ?? {};
370
+ // eslint-disable-next-line prefer-const
371
+ let { operation, operand1, operand2 } = groups;
372
+ if (operation && operand1 && operand2) {
373
+ if (operand1 === '__today__') {
374
+ operand1 = today;
375
+ }
376
+ else if (operand1.split('.').length > 1) {
377
+ const propertyId = operand1.split('.')[1];
378
+ if (propertyId in this.root.data) {
379
+ operand1 = this.root.data[propertyId];
380
+ }
381
+ }
382
+ else {
383
+ throw `Invalid operand: ${operand1}`;
384
+ }
385
+ if (operation === 'addDays') {
386
+ return addDays(operand1, parseInt(operand2));
387
+ }
388
+ else {
389
+ return subDays(operand1, parseInt(operand2));
390
+ }
391
+ }
392
+ }
393
+ throw `Invalid date validation: ${dateValidation}`;
366
394
  }
367
395
  catch (err) {
368
396
  console.log(err);
397
+ return undefined;
369
398
  }
370
399
  }
400
+ };
401
+ if (this.component.validate.minDate || this.component.validate.maxDate) {
402
+ const minDate = validate.minDate ? evaluateDateValidation(validate.minDate) : undefined;
403
+ const maxDate = validate.maxDate ? evaluateDateValidation(validate.maxDate) : undefined;
371
404
  if ((minDate && value < minDate) || (maxDate && value > maxDate)) {
372
405
  this.errorDetails['date'] = validate.customMessage;
373
406
  }
@@ -423,7 +456,11 @@ export class FormFieldComponent extends ReactComponent {
423
456
  !this.root.customErrors.some((err) => err.message === error)) {
424
457
  this.root.customErrors = [
425
458
  ...this.root.customErrors,
426
- { component: this.component, message: error, formattedKeyOrPath: this.component.key },
459
+ {
460
+ component: this.component,
461
+ message: error,
462
+ formattedKeyOrPath: this.component.key,
463
+ },
427
464
  ];
428
465
  }
429
466
  });
@@ -1,5 +1,5 @@
1
1
  import cleanDeep from 'clean-deep';
2
- import Handlebars from 'handlebars';
2
+ import Handlebars from 'kbn-handlebars';
3
3
  import { cloneDeep, debounce, isEmpty, isNil } from 'lodash';
4
4
  import React, { useCallback, useEffect, useState } from 'react';
5
5
  import { Close } from '../../../../../icons';
@@ -132,7 +132,7 @@ export const ObjectPropertyInput = (props) => {
132
132
  setOpenCreateDialog(false);
133
133
  };
134
134
  const compileExpression = (expression, instance) => {
135
- const template = Handlebars.compile(expression);
135
+ const template = Handlebars.compileAST(expression);
136
136
  return instance ? template(instance) : undefined;
137
137
  };
138
138
  const navigationSlug = defaultPages && property.objectId && defaultPages[property.objectId];
@@ -1,4 +1,4 @@
1
- import Handlebars from 'handlebars';
1
+ import Handlebars from 'kbn-handlebars';
2
2
  import { difference, isEmpty, isObject } from 'lodash';
3
3
  import React, { useEffect, useState } from 'react';
4
4
  import { FormField } from '../../../..';
@@ -60,7 +60,7 @@ export const DropdownRepeatableFieldInput = (props) => {
60
60
  }
61
61
  };
62
62
  const compileExpression = (instance, expression) => {
63
- const template = Handlebars.compile(expression);
63
+ const template = Handlebars.compileAST(expression);
64
64
  return template(instance);
65
65
  };
66
66
  return (React.createElement(React.Fragment, null, !readOnly ? (property && (React.createElement(React.Fragment, null,
@@ -87,5 +87,8 @@ export const DropdownRepeatableFieldInput = (props) => {
87
87
  } })),
88
88
  loading: loading,
89
89
  } }),
90
- React.createElement(Snackbar, { open: snackbarError.showAlert, handleClose: () => setSnackbarError({ isError: snackbarError.isError, showAlert: false }), message: snackbarError.message, error: snackbarError.isError })))) : (React.createElement(Typography, null, selectedOptions && selectedOptions.map((option) => option.label).join(', ')))));
90
+ React.createElement(Snackbar, { open: snackbarError.showAlert, handleClose: () => setSnackbarError({
91
+ isError: snackbarError.isError,
92
+ showAlert: false,
93
+ }), message: snackbarError.message, error: snackbarError.isError })))) : (React.createElement(Typography, null, selectedOptions && selectedOptions.map((option) => option.label).join(', ')))));
91
94
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evoke-platform/ui-components",
3
- "version": "1.0.0-dev.239",
3
+ "version": "1.0.0-dev.241",
4
4
  "description": "",
5
5
  "main": "dist/published/index.js",
6
6
  "module": "dist/published/index.js",
@@ -114,8 +114,8 @@
114
114
  "eslint-plugin-no-inline-styles": "^1.0.5",
115
115
  "flat": "^6.0.1",
116
116
  "formiojs": "^4.15.0-rc.23",
117
- "handlebars": "^4.7.8",
118
117
  "html-react-parser": "^5.1.18",
118
+ "kbn-handlebars": "^1.0.0",
119
119
  "luxon": "^2.5.2",
120
120
  "nanoid": "^5.0.8",
121
121
  "nanoid-dictionary": "^4.3.0",