@evoke-platform/ui-components 1.0.0-dev.123 → 1.0.0-dev.125

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.
@@ -0,0 +1,10 @@
1
+ import { ReactComponent } from '@formio/react';
2
+ declare class Buttons extends ReactComponent {
3
+ [x: string]: any;
4
+ constructor(component: any, options: any, data: any);
5
+ init(): void;
6
+ handleCancel: (event: any) => void;
7
+ handleSubmit: (event: any) => void;
8
+ attachReact(element: any): void;
9
+ }
10
+ export default Buttons;
@@ -0,0 +1,37 @@
1
+ import React from 'react';
2
+ import { Button as EvokeButton } from '../components/core';
3
+ import { ReactComponent } from '@formio/react';
4
+ import ReactDOM from 'react-dom';
5
+ import { cloneDeep, isEmpty, isEqual } from 'lodash';
6
+ class Buttons extends ReactComponent {
7
+ constructor(component, options, data) {
8
+ super(component, options, data);
9
+ this.handleCancel = (event) => {
10
+ this.component.components[0].onCancel(event);
11
+ };
12
+ this.handleSubmit = (event) => {
13
+ const submission = this.root.getValue();
14
+ this.emit('submitButton', submission);
15
+ if (isEmpty([...this.root.errors, ...this.root.customErrors])) {
16
+ this.component.components[1].onSubmit(submission);
17
+ }
18
+ };
19
+ this.handleCancel = this.handleCancel.bind(this);
20
+ this.handleSubmit = this.handleSubmit.bind(this);
21
+ }
22
+ init() {
23
+ const originalData = cloneDeep(this.root.data);
24
+ this.on('change', () => {
25
+ if (!isEqual(originalData, this.root.data)) {
26
+ this.root.formEdited = true;
27
+ this.attachReact(this.element);
28
+ }
29
+ });
30
+ }
31
+ attachReact(element) {
32
+ return ReactDOM.render(React.createElement("div", { style: { width: '100%', display: 'flex', justifyContent: 'flex-end' } },
33
+ React.createElement(EvokeButton, { sx: { margin: '5px' }, variant: this.component.components[0].variant, onClick: this.handleCancel, disabled: !this.root.formEdited }, this.component.components[0].label),
34
+ React.createElement(EvokeButton, { sx: { margin: '5px' }, variant: this.component.components[1].variant, onClick: this.handleSubmit, disabled: !this.root.formEdited }, this.component.components[1].label)), element);
35
+ }
36
+ }
37
+ export default Buttons;
@@ -0,0 +1,21 @@
1
+ /// <reference types="react" />
2
+ declare type FieldWrapperProps = {
3
+ label: string;
4
+ description?: string;
5
+ tooltip?: string;
6
+ prefix?: string;
7
+ suffix?: string;
8
+ value?: string;
9
+ validate: any;
10
+ errorMessage: string;
11
+ showCharCount?: boolean;
12
+ type: string;
13
+ onChange: Function;
14
+ children: any;
15
+ };
16
+ /**
17
+ * A component that wraps a FormField and adds a label,
18
+ * description, tooltip, prefix, suffix and word/char counts
19
+ */
20
+ declare const FieldWrapper: (props: FieldWrapperProps) => JSX.Element;
21
+ export default FieldWrapper;
@@ -0,0 +1,84 @@
1
+ import { Typography, Tooltip, IconButton } from '../components/core';
2
+ import { Box } from '../components/layout';
3
+ import { Help } from '../icons';
4
+ import React, { useEffect, useRef } from 'react';
5
+ const PrefixSuffix = (props) => {
6
+ const { prefix, suffix, height } = props;
7
+ const text = prefix || suffix;
8
+ const prefixSuffixStyles = Object.assign(Object.assign(Object.assign({ display: 'inline-flex', alignItems: 'center', background: '#f5f5f5', padding: '5px', border: '1px solid #ccc' }, (suffix && {
9
+ borderTopRightRadius: '5px',
10
+ borderBottomRightRadius: '5px',
11
+ marginLeft: '-5px',
12
+ paddingLeft: '10px',
13
+ })), (prefix && {
14
+ borderTopLeftRadius: '5px',
15
+ borderBottomLeftRadius: '5px',
16
+ marginRight: '-5px',
17
+ paddingRight: '10px',
18
+ })), { height: height });
19
+ if (!prefix && !suffix)
20
+ return null;
21
+ return (React.createElement(Box, { sx: prefixSuffixStyles },
22
+ React.createElement(Typography, null, text)));
23
+ };
24
+ /**
25
+ * A component that wraps a FormField and adds a label,
26
+ * description, tooltip, prefix, suffix and word/char counts
27
+ */
28
+ const FieldWrapper = (props) => {
29
+ const { label, description, tooltip, prefix, suffix, value, validate, errorMessage, showCharCount, type, onChange, children, } = props;
30
+ const [fieldHeight, setFieldHeight] = React.useState(40);
31
+ const [fieldValue, setFieldValue] = React.useState(value);
32
+ const { maxLength } = validate;
33
+ const fieldRef = useRef(null);
34
+ useEffect(() => {
35
+ //get height of field to sync with height of prefix and suffix
36
+ const fieldElement = fieldRef.current;
37
+ if (fieldElement && (prefix || suffix)) {
38
+ setFieldHeight(fieldElement.getBoundingClientRect().height - 12);
39
+ }
40
+ }, [fieldRef]);
41
+ const underFieldStyles = {
42
+ display: 'flex',
43
+ flexDirection: 'row',
44
+ justifyContent: 'space-between',
45
+ alignItems: 'center',
46
+ };
47
+ const descriptionStyles = {
48
+ color: '#999',
49
+ whiteSpace: 'normal',
50
+ };
51
+ let charCount = fieldValue && type === 'textfield' ? fieldValue.length : 0;
52
+ if (maxLength)
53
+ charCount = maxLength - charCount;
54
+ const handleChange = (key, value) => {
55
+ onChange(key, value, new Event('keydown'));
56
+ setFieldValue(value);
57
+ };
58
+ return (React.createElement(Box, null,
59
+ React.createElement(Box, null,
60
+ React.createElement(Typography, { sx: { fontWeight: 600 }, variant: "subtitle2" },
61
+ label,
62
+ validate.required ? React.createElement("span", { style: { color: 'red' } },
63
+ ` *`,
64
+ " ") : null,
65
+ tooltip && (React.createElement(Tooltip, { placement: "right", title: tooltip },
66
+ React.createElement(IconButton, null,
67
+ React.createElement(Help, { sx: { fontSize: '14px' } }))))),
68
+ React.createElement(Typography, { variant: "caption", sx: descriptionStyles }, description),
69
+ React.createElement(Box, { sx: { display: 'flex', flexDirection: 'row' } },
70
+ React.createElement(PrefixSuffix, { prefix: prefix, height: fieldHeight }),
71
+ React.createElement("div", { style: { width: '100%' }, ref: fieldRef }, React.Children.map(children, (child) => {
72
+ return React.cloneElement(child, { onChange: handleChange });
73
+ })),
74
+ React.createElement(PrefixSuffix, { suffix: suffix, height: fieldHeight }))),
75
+ React.createElement(Box, { sx: underFieldStyles },
76
+ React.createElement(Box, { sx: { width: '100%', display: 'flex', justifyContent: 'space-between' } },
77
+ errorMessage ? (React.createElement(Typography, { sx: { color: 'red', whiteSpace: 'normal' }, variant: "caption" }, ' ⚠ ' + errorMessage)) : (React.createElement("span", null)),
78
+ showCharCount && (React.createElement(Typography, { variant: "caption", sx: { color: '#999', whiteSpace: 'nowrap' } },
79
+ charCount,
80
+ " ",
81
+ charCount === 1 ? 'character' : 'characters',
82
+ !!maxLength && ` remaining`))))));
83
+ };
84
+ export default FieldWrapper;
@@ -0,0 +1,34 @@
1
+ import { ReactComponent } from '@formio/react';
2
+ declare class FormFieldComponent extends ReactComponent {
3
+ [x: string]: any;
4
+ static schema: any;
5
+ errorDetails: any;
6
+ /**
7
+ * Called when the component has been instantiated. This is useful to define
8
+ * default instance variable values.
9
+ *
10
+ * @param component - The JSON representation of the component created.
11
+ * @param options - The global options for the renderer
12
+ * @param data - The contextual data object (model) used for this component.
13
+ */
14
+ constructor(component: any, options: any, data: any);
15
+ init(): void;
16
+ handleValidation(value: string): void;
17
+ hasCustomErrors(): boolean;
18
+ manageFormErrors(): void;
19
+ beforeSubmit(): void;
20
+ /**
21
+ * Checks for errors in the data value.
22
+ * @param {any} data - The data value to check.
23
+ * @param {boolean} dirty - Whether the data value has been modified.
24
+ * @param {any} rowData - The row data.
25
+ * @returns {{ hasErrors: boolean, fieldErrorMessages: string[] }} An object containing whether the data value has errors and an array of error messages.
26
+ */
27
+ errorCheck(data: any, dirty: any, rowData: any): {
28
+ hasErrors: boolean;
29
+ fieldErrorMessages: any[];
30
+ };
31
+ handleChange: (key: string, value: any, event: Event) => void;
32
+ attachReact(element: any): void;
33
+ }
34
+ export default FormFieldComponent;
@@ -0,0 +1,160 @@
1
+ import React from 'react';
2
+ import { FormField } from '../components/custom';
3
+ import FieldWrapper from './FieldWrapper';
4
+ import { isEmpty } from 'lodash';
5
+ import ReactDOM from 'react-dom';
6
+ import { ReactComponent } from '@formio/react';
7
+ class FormFieldComponent extends ReactComponent {
8
+ /**
9
+ * Called when the component has been instantiated. This is useful to define
10
+ * default instance variable values.
11
+ *
12
+ * @param component - The JSON representation of the component created.
13
+ * @param options - The global options for the renderer
14
+ * @param data - The contextual data object (model) used for this component.
15
+ */
16
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
17
+ constructor(component, options, data) {
18
+ const { property } = component;
19
+ component.property = Object.assign(Object.assign({}, component.property), { type: property.type === 'string' && property.enum ? 'choices' : property.type });
20
+ let selectOptions = [];
21
+ if (!isEmpty(component.data)) {
22
+ selectOptions = component.data.values;
23
+ }
24
+ else if (property.enum) {
25
+ selectOptions = property.enum.map((val) => ({ label: val, value: val }));
26
+ }
27
+ super(Object.assign(Object.assign({}, component), { hideLabel: true, readOnly: component.readOnlyValue || !!property.formula, selectOptions }), options, data);
28
+ this.handleChange = (key, value, event) => {
29
+ event.stopPropagation();
30
+ const val = typeof value === 'object' && value.hasOwnProperty('value') ? value.value : value;
31
+ this.updateValue(val, { modified: true });
32
+ this.emit('changed-' + this.component.key, val);
33
+ this.handleValidation(val);
34
+ this.attachReact(this.element);
35
+ };
36
+ this.errorDetails = {};
37
+ this.handleChange = this.handleChange.bind(this);
38
+ }
39
+ init() {
40
+ const { conditional: { when, eq }, } = this.component;
41
+ //listens for the this.emit('changed-'... event on the 'when' component
42
+ this.on('changed-' + when, (value) => {
43
+ if (when && eq && value === eq) {
44
+ this.component.hidden = false;
45
+ this.attachReact(this.element);
46
+ }
47
+ });
48
+ }
49
+ handleValidation(value) {
50
+ const { validate } = this.component;
51
+ if ((value === undefined || value === null || value === '') && !validate.required) {
52
+ return;
53
+ }
54
+ if (!isEmpty(validate.regexes)) {
55
+ const regexes = validate.regexes;
56
+ const failedRules = validate.regexes.filter((rule) => {
57
+ if (!RegExp(rule.regex).test(value)) {
58
+ return true;
59
+ }
60
+ return false;
61
+ });
62
+ if (failedRules.length === 0) {
63
+ delete this.errorDetails['regexes'];
64
+ }
65
+ else {
66
+ if (validate.operator === 'all') {
67
+ this.errorDetails['regexes'] = failedRules.map((rule) => rule.errorMessage).join(' and ');
68
+ }
69
+ else if (validate.operator === 'any') {
70
+ if (regexes.length > failedRules.length) {
71
+ delete this.errorDetails['regexes'];
72
+ }
73
+ else {
74
+ this.errorDetails['regexes'] = failedRules.map((rule) => rule.errorMessage).join(' or ');
75
+ }
76
+ }
77
+ }
78
+ }
79
+ if (this.component.validate.minDate) {
80
+ if (value < this.component.validate.minDate || value > this.component.validate.maxDate) {
81
+ this.errorDetails['date'] = this.component.validate.customMessage;
82
+ }
83
+ else {
84
+ delete this.errorDetails['date'];
85
+ }
86
+ }
87
+ if (this.component.validate.min && value < this.component.validate.min) {
88
+ this.errorDetails['min-max'] = this.component.validate.customMessage;
89
+ }
90
+ else if (this.component.validate.max && value > this.component.validate.max) {
91
+ this.errorDetails['min-max'] = this.component.validate.customMessage;
92
+ }
93
+ else {
94
+ delete this.errorDetails['min-max'];
95
+ }
96
+ this.manageFormErrors();
97
+ }
98
+ hasCustomErrors() {
99
+ return !isEmpty(this.errorDetails);
100
+ }
101
+ manageFormErrors() {
102
+ if (!isEmpty(this.errorDetails)) {
103
+ const fieldError = {
104
+ component: this.component,
105
+ external: false,
106
+ formattedKeyOrPath: this.component.key,
107
+ message: this.component.label + ': ' + Object.values(this.errorDetails).join(', '),
108
+ };
109
+ const rootHasErrorData = this.root.customErrors.some((error) => {
110
+ return error.formattedKeyOrPath === this.component.key;
111
+ });
112
+ if (!rootHasErrorData) {
113
+ this.root.customErrors = [...this.root.customErrors, fieldError];
114
+ }
115
+ }
116
+ else {
117
+ this.root.customErrors = this.root.customErrors.filter((error) => {
118
+ return error.formattedKeyOrPath !== this.component.key;
119
+ });
120
+ }
121
+ }
122
+ beforeSubmit() {
123
+ this.handleValidation(this.dataValue);
124
+ this.attachReact(this.element);
125
+ }
126
+ /**
127
+ * Checks for errors in the data value.
128
+ * @param {any} data - The data value to check.
129
+ * @param {boolean} dirty - Whether the data value has been modified.
130
+ * @param {any} rowData - The row data.
131
+ * @returns {{ hasErrors: boolean, fieldErrorMessages: string[] }} An object containing whether the data value has errors and an array of error messages.
132
+ */
133
+ errorCheck(data, dirty, rowData) {
134
+ //after changes, check for both custom errors and formio errors
135
+ if (this.dataValue !== this.defaultValue && !this.component.readOnlyValue) {
136
+ return {
137
+ hasErrors: this.hasCustomErrors() || !super.checkValidity(data, dirty, rowData),
138
+ fieldErrorMessages: [...this.root.errors, ...this.root.customErrors]
139
+ .filter((error) => error.component.key === this.component.key)
140
+ .map((error) => error.message),
141
+ };
142
+ }
143
+ return {
144
+ hasErrors: false,
145
+ fieldErrorMessages: [],
146
+ };
147
+ }
148
+ attachReact(element) {
149
+ var _a;
150
+ const { inputMask, hidden } = this.component;
151
+ const { hasErrors, fieldErrorMessages } = this.errorCheck(this.dataValue, false, this.data);
152
+ let root = ReactDOM.findDOMNode(element);
153
+ if (!root) {
154
+ root = element;
155
+ }
156
+ return ReactDOM.render(React.createElement("div", null, !hidden ? (React.createElement(FieldWrapper, Object.assign({}, this.component, { onChange: this.handleChange, error: hasErrors, errorMessage: fieldErrorMessages.join(', ') }),
157
+ React.createElement(FormField, Object.assign({}, this.component, { defaultValue: (_a = this.dataValue) !== null && _a !== void 0 ? _a : this.component.defaultValue, mask: inputMask, error: hasErrors })))) : null), root);
158
+ }
159
+ }
160
+ export default FormFieldComponent;
@@ -0,0 +1,21 @@
1
+ /// <reference types="react" />
2
+ import FormFieldComponent from './FormFieldComponent';
3
+ import Buttons from './Buttons';
4
+ export declare const FormComponents: {
5
+ FormFieldComponent: typeof FormFieldComponent;
6
+ Buttons: typeof Buttons;
7
+ FieldWrapper: (props: {
8
+ label: string;
9
+ description?: string | undefined;
10
+ tooltip?: string | undefined;
11
+ prefix?: string | undefined;
12
+ suffix?: string | undefined;
13
+ value?: string | undefined;
14
+ validate: any;
15
+ errorMessage: string;
16
+ showCharCount?: boolean | undefined;
17
+ type: string;
18
+ onChange: Function;
19
+ children: any;
20
+ }) => JSX.Element;
21
+ };
@@ -0,0 +1,8 @@
1
+ import FormFieldComponent from './FormFieldComponent';
2
+ import Buttons from './Buttons';
3
+ import FieldWrapper from './FieldWrapper';
4
+ export const FormComponents = {
5
+ FormFieldComponent,
6
+ Buttons,
7
+ FieldWrapper,
8
+ };
@@ -1,6 +1,7 @@
1
- export { ClickAwayListener, FormControlProps, FormHelperTextProps, GridSize, Toolbar, createTheme } from '@mui/material';
1
+ export { ClickAwayListener, FormControlProps, FormHelperTextProps, GridSize, Toolbar, createTheme, } from '@mui/material';
2
2
  export { CalendarPicker, DateTimePicker, MonthPicker, PickersDay, StaticDateTimePicker, StaticTimePicker, TimePicker, YearPicker, } from '@mui/x-date-pickers';
3
+ export { FormComponents } from './form-components';
3
4
  export * from './components/core';
4
- export { CriteriaBuilder, DataGrid, FormField, MenuBar, MultiSelect, RepeatableField, UserAvatar } from './components/custom';
5
+ export { CriteriaBuilder, DataGrid, FormField, MenuBar, MultiSelect, RepeatableField, UserAvatar, } from './components/custom';
5
6
  export { Box, Container, Grid, Stack } from './components/layout';
6
7
  export * from './util';
@@ -1,6 +1,7 @@
1
- export { ClickAwayListener, Toolbar, createTheme } from '@mui/material';
1
+ export { ClickAwayListener, Toolbar, createTheme, } from '@mui/material';
2
2
  export { CalendarPicker, DateTimePicker, MonthPicker, PickersDay, StaticDateTimePicker, StaticTimePicker, TimePicker, YearPicker, } from '@mui/x-date-pickers';
3
+ export { FormComponents } from './form-components';
3
4
  export * from './components/core';
4
- export { CriteriaBuilder, DataGrid, FormField, MenuBar, MultiSelect, RepeatableField, UserAvatar } from './components/custom';
5
+ export { CriteriaBuilder, DataGrid, FormField, MenuBar, MultiSelect, RepeatableField, UserAvatar, } from './components/custom';
5
6
  export { Box, Container, Grid, Stack } from './components/layout';
6
7
  export * from './util';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evoke-platform/ui-components",
3
- "version": "1.0.0-dev.123",
3
+ "version": "1.0.0-dev.125",
4
4
  "description": "",
5
5
  "main": "dist/published/index.js",
6
6
  "module": "dist/published/index.js",
@@ -63,6 +63,7 @@
63
63
  "@dnd-kit/sortable": "^7.0.1",
64
64
  "@emotion/react": "^11.9.0",
65
65
  "@emotion/styled": "^11.8.1",
66
+ "@formio/react": "^5.2.4-rc.1",
66
67
  "@js-joda/core": "^3.2.0",
67
68
  "@js-joda/locale_en-us": "^3.2.2",
68
69
  "@mui/icons-material": "^5.8.4",
@@ -72,12 +73,13 @@
72
73
  "@mui/x-date-pickers": "^5.0.13",
73
74
  "@react-querybuilder/dnd": "^5.4.1",
74
75
  "@react-querybuilder/material": "^5.4.1",
76
+ "@types/react-input-mask": "^3.0.2",
77
+ "formiojs": "^4.15.0-rc.23",
75
78
  "lodash-es": "^4.17.21",
76
79
  "react-dropzone": "^14.2.2",
77
80
  "react-input-mask": "^2.0.4",
78
- "@types/react-input-mask": "^3.0.2",
79
- "react-querybuilder": "^6.0.2",
80
- "react-number-format": "^4.9.3"
81
+ "react-number-format": "^4.9.3",
82
+ "react-querybuilder": "^6.0.2"
81
83
  },
82
84
  "lint-staged": {
83
85
  "*": "prettier --write"