diff --git a/spiffworkflow-frontend/package-lock.json b/spiffworkflow-frontend/package-lock.json index f03d5781..bb88fc99 100644 --- a/spiffworkflow-frontend/package-lock.json +++ b/spiffworkflow-frontend/package-lock.json @@ -18,6 +18,7 @@ "@casl/react": "^3.1.0", "@ginkgo-bioworks/react-json-schema-form-builder": "^2.9.0", "@monaco-editor/react": "^4.4.5", + "@mui/material": "^5.10.14", "@react-icons/all-files": "^4.1.0", "@rjsf/core": "*", "@rjsf/mui": "^5.0.0-beta.13", @@ -4447,7 +4448,6 @@ "version": "5.0.0-alpha.106", "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.106.tgz", "integrity": "sha512-xJQQtwPCPwr6hGWTBdvDwHYwExn3Bw7nPQkN8Fuz8kHpZqoMVWQvvaFS557AIkkI2AFLV3DxVIMjbCvrIntBWg==", - "peer": true, "dependencies": { "@babel/runtime": "^7.20.1", "@emotion/is-prop-valid": "^1.2.0", @@ -4480,7 +4480,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.0.tgz", "integrity": "sha512-3aDpDprjM0AwaxGE09bOPkNxHpBd+kA6jty3RnaEXdweX1DF1U3VQpPYb0g1IStAuK7SVQ1cy+bNBBKp4W3Fjg==", - "peer": true, "dependencies": { "@emotion/memoize": "^0.8.0" } @@ -4488,20 +4487,17 @@ "node_modules/@mui/base/node_modules/@emotion/memoize": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.0.tgz", - "integrity": "sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA==", - "peer": true + "integrity": "sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA==" }, "node_modules/@mui/base/node_modules/react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "peer": true + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" }, "node_modules/@mui/core-downloads-tracker": { "version": "5.10.14", "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.10.14.tgz", "integrity": "sha512-qLgIJNOR9Dre8JiZ/neVzOf4jf88J6YtOkQqugtMrleLjbfRVUSS4LWl9CSOjNq76quYdmYWnSDgfQqOooT2cQ==", - "peer": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/mui" @@ -4537,7 +4533,6 @@ "version": "5.10.14", "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.10.14.tgz", "integrity": "sha512-HWzKVAykePMx54WtxVwZyL1W4k3xlHYIqwMw0CaXAvgB3UE9yjABZuuGr8vG5Z6CSNWamzd+s1x8u7pQPFl9og==", - "peer": true, "dependencies": { "@babel/runtime": "^7.20.1", "@mui/base": "5.0.0-alpha.106", @@ -4581,14 +4576,12 @@ "node_modules/@mui/material/node_modules/react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "peer": true + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" }, "node_modules/@mui/private-theming": { "version": "5.10.14", "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.10.14.tgz", "integrity": "sha512-3aIBe8WK65CwAPDY8nB11hYnzE1CZMymi76UnaFrA/DdGDwl5Y8F6uB+StKrkVmsqF1po7Mp2odqVkHj320gXw==", - "peer": true, "dependencies": { "@babel/runtime": "^7.20.1", "@mui/utils": "^5.10.14", @@ -4615,7 +4608,6 @@ "version": "5.10.14", "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.10.14.tgz", "integrity": "sha512-bgKdM57ExogWpIfhL/ngSlzF4FhbH00vYF+Y5VALTob4uslFqje0xzoWmbfcCn4cZt2NXxZJIwhsq4vzo5itlw==", - "peer": true, "dependencies": { "@babel/runtime": "^7.20.1", "@emotion/cache": "^11.10.5", @@ -4647,7 +4639,6 @@ "version": "5.10.14", "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.10.14.tgz", "integrity": "sha512-2de7XCjRb1j8Od0Stmo0LwFMLpOMNT4wzfINuExXI1TVSuyxXIXUxiC5FEgJW3GMvf/a7SUR8VOiMoKlKWzukw==", - "peer": true, "dependencies": { "@babel/runtime": "^7.20.1", "@mui/private-theming": "^5.10.14", @@ -4687,7 +4678,6 @@ "version": "7.2.1", "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.1.tgz", "integrity": "sha512-c5mSM7ivD8EsqK6HUi9hQPr5V7TJ/IRThUQ9nWNYPdhCGriTSQV4vL6DflT99LkM+wLiIS1rVjphpEWxERep7A==", - "peer": true, "peerDependencies": { "@types/react": "*" }, @@ -4701,7 +4691,6 @@ "version": "5.10.14", "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.10.14.tgz", "integrity": "sha512-12p59+wDZpA++XVJmKwqsZmrA1nmUQ5d0a1yQWtcDjxNyER1EDzozYN/db+FY2i5ceQh2TynPTEwGms2mXDwFg==", - "peer": true, "dependencies": { "@babel/runtime": "^7.20.1", "@types/prop-types": "^15.7.5", @@ -4723,8 +4712,7 @@ "node_modules/@mui/utils/node_modules/react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "peer": true + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", @@ -5921,7 +5909,6 @@ "version": "17.0.3", "resolved": "https://registry.npmjs.org/@types/react-is/-/react-is-17.0.3.tgz", "integrity": "sha512-aBTIWg1emtu95bLTLx0cpkxwGW3ueZv71nE2YFBpL8k/z5czEW8yYpOo8Dp+UUAFAtKwNaOsh/ioSeQnWlZcfw==", - "peer": true, "dependencies": { "@types/react": "*" } @@ -8977,7 +8964,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", - "peer": true, "engines": { "node": ">=6" } @@ -34603,7 +34589,6 @@ "version": "5.0.0-alpha.106", "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.106.tgz", "integrity": "sha512-xJQQtwPCPwr6hGWTBdvDwHYwExn3Bw7nPQkN8Fuz8kHpZqoMVWQvvaFS557AIkkI2AFLV3DxVIMjbCvrIntBWg==", - "peer": true, "requires": { "@babel/runtime": "^7.20.1", "@emotion/is-prop-valid": "^1.2.0", @@ -34619,7 +34604,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.0.tgz", "integrity": "sha512-3aDpDprjM0AwaxGE09bOPkNxHpBd+kA6jty3RnaEXdweX1DF1U3VQpPYb0g1IStAuK7SVQ1cy+bNBBKp4W3Fjg==", - "peer": true, "requires": { "@emotion/memoize": "^0.8.0" } @@ -34627,22 +34611,19 @@ "@emotion/memoize": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.0.tgz", - "integrity": "sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA==", - "peer": true + "integrity": "sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA==" }, "react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "peer": true + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" } } }, "@mui/core-downloads-tracker": { "version": "5.10.14", "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.10.14.tgz", - "integrity": "sha512-qLgIJNOR9Dre8JiZ/neVzOf4jf88J6YtOkQqugtMrleLjbfRVUSS4LWl9CSOjNq76quYdmYWnSDgfQqOooT2cQ==", - "peer": true + "integrity": "sha512-qLgIJNOR9Dre8JiZ/neVzOf4jf88J6YtOkQqugtMrleLjbfRVUSS4LWl9CSOjNq76quYdmYWnSDgfQqOooT2cQ==" }, "@mui/icons-material": { "version": "5.10.14", @@ -34657,7 +34638,6 @@ "version": "5.10.14", "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.10.14.tgz", "integrity": "sha512-HWzKVAykePMx54WtxVwZyL1W4k3xlHYIqwMw0CaXAvgB3UE9yjABZuuGr8vG5Z6CSNWamzd+s1x8u7pQPFl9og==", - "peer": true, "requires": { "@babel/runtime": "^7.20.1", "@mui/base": "5.0.0-alpha.106", @@ -34676,8 +34656,7 @@ "react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "peer": true + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" } } }, @@ -34685,7 +34664,6 @@ "version": "5.10.14", "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.10.14.tgz", "integrity": "sha512-3aIBe8WK65CwAPDY8nB11hYnzE1CZMymi76UnaFrA/DdGDwl5Y8F6uB+StKrkVmsqF1po7Mp2odqVkHj320gXw==", - "peer": true, "requires": { "@babel/runtime": "^7.20.1", "@mui/utils": "^5.10.14", @@ -34696,7 +34674,6 @@ "version": "5.10.14", "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.10.14.tgz", "integrity": "sha512-bgKdM57ExogWpIfhL/ngSlzF4FhbH00vYF+Y5VALTob4uslFqje0xzoWmbfcCn4cZt2NXxZJIwhsq4vzo5itlw==", - "peer": true, "requires": { "@babel/runtime": "^7.20.1", "@emotion/cache": "^11.10.5", @@ -34708,7 +34685,6 @@ "version": "5.10.14", "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.10.14.tgz", "integrity": "sha512-2de7XCjRb1j8Od0Stmo0LwFMLpOMNT4wzfINuExXI1TVSuyxXIXUxiC5FEgJW3GMvf/a7SUR8VOiMoKlKWzukw==", - "peer": true, "requires": { "@babel/runtime": "^7.20.1", "@mui/private-theming": "^5.10.14", @@ -34724,14 +34700,12 @@ "version": "7.2.1", "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.1.tgz", "integrity": "sha512-c5mSM7ivD8EsqK6HUi9hQPr5V7TJ/IRThUQ9nWNYPdhCGriTSQV4vL6DflT99LkM+wLiIS1rVjphpEWxERep7A==", - "peer": true, "requires": {} }, "@mui/utils": { "version": "5.10.14", "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.10.14.tgz", "integrity": "sha512-12p59+wDZpA++XVJmKwqsZmrA1nmUQ5d0a1yQWtcDjxNyER1EDzozYN/db+FY2i5ceQh2TynPTEwGms2mXDwFg==", - "peer": true, "requires": { "@babel/runtime": "^7.20.1", "@types/prop-types": "^15.7.5", @@ -34743,8 +34717,7 @@ "react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "peer": true + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" } } }, @@ -35627,7 +35600,6 @@ "version": "17.0.3", "resolved": "https://registry.npmjs.org/@types/react-is/-/react-is-17.0.3.tgz", "integrity": "sha512-aBTIWg1emtu95bLTLx0cpkxwGW3ueZv71nE2YFBpL8k/z5czEW8yYpOo8Dp+UUAFAtKwNaOsh/ioSeQnWlZcfw==", - "peer": true, "requires": { "@types/react": "*" } @@ -37942,8 +37914,7 @@ "clsx": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", - "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", - "peer": true + "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==" }, "co": { "version": "4.6.0", diff --git a/spiffworkflow-frontend/package.json b/spiffworkflow-frontend/package.json index ebc77e74..00a2cb74 100644 --- a/spiffworkflow-frontend/package.json +++ b/spiffworkflow-frontend/package.json @@ -13,6 +13,7 @@ "@casl/react": "^3.1.0", "@ginkgo-bioworks/react-json-schema-form-builder": "^2.9.0", "@monaco-editor/react": "^4.4.5", + "@mui/material": "^5.10.14", "@react-icons/all-files": "^4.1.0", "@rjsf/core": "*", "@rjsf/mui": "^5.0.0-beta.13", diff --git a/spiffworkflow-frontend/src/routes/TaskShow.tsx b/spiffworkflow-frontend/src/routes/TaskShow.tsx index b64880a7..81867095 100644 --- a/spiffworkflow-frontend/src/routes/TaskShow.tsx +++ b/spiffworkflow-frontend/src/routes/TaskShow.tsx @@ -1,7 +1,5 @@ import { useContext, useEffect, useState } from 'react'; import { Link, useNavigate, useParams } from 'react-router-dom'; -// import Form from '@rjsf/core'; -import Form from '@rjsf/mui'; import validator from '@rjsf/validator-ajv8'; // @ts-ignore @@ -9,7 +7,7 @@ import { Button, Stack } from '@carbon/react'; import ReactMarkdown from 'react-markdown'; import remarkGfm from 'remark-gfm'; -// import Form from '../themes'; +import Form from '../themes/carbon'; import HttpService from '../services/HttpService'; import ErrorContext from '../contexts/ErrorContext'; import { modifyProcessModelPath } from '../helpers'; diff --git a/spiffworkflow-frontend/src/themes/carbon/AddButton/AddButton.tsx b/spiffworkflow-frontend/src/themes/carbon/AddButton/AddButton.tsx new file mode 100644 index 00000000..d145a4c4 --- /dev/null +++ b/spiffworkflow-frontend/src/themes/carbon/AddButton/AddButton.tsx @@ -0,0 +1,17 @@ +import React from 'react'; +import AddIcon from '@mui/icons-material/Add'; +import IconButton from '@mui/material/IconButton'; +import { IconButtonProps } from '@rjsf/utils'; + +const AddButton: React.ComponentType = ({ + uiSchema, + ...props +}) => { + return ( + + + + ); +}; + +export default AddButton; diff --git a/spiffworkflow-frontend/src/themes/carbon/AddButton/index.ts b/spiffworkflow-frontend/src/themes/carbon/AddButton/index.ts new file mode 100644 index 00000000..752d720d --- /dev/null +++ b/spiffworkflow-frontend/src/themes/carbon/AddButton/index.ts @@ -0,0 +1,2 @@ +export { default } from './AddButton'; +export * from './AddButton'; diff --git a/spiffworkflow-frontend/src/themes/carbon/ArrayFieldItemTemplate/ArrayFieldItemTemplate.tsx b/spiffworkflow-frontend/src/themes/carbon/ArrayFieldItemTemplate/ArrayFieldItemTemplate.tsx new file mode 100644 index 00000000..b09acfb9 --- /dev/null +++ b/spiffworkflow-frontend/src/themes/carbon/ArrayFieldItemTemplate/ArrayFieldItemTemplate.tsx @@ -0,0 +1,72 @@ +import React, { CSSProperties } from 'react'; +import Box from '@mui/material/Box'; +import Grid from '@mui/material/Grid'; +import Paper from '@mui/material/Paper'; +import { ArrayFieldTemplateItemType } from '@rjsf/utils'; + +function ArrayFieldItemTemplate(props: ArrayFieldTemplateItemType) { + const { + children, + disabled, + hasToolbar, + hasMoveDown, + hasMoveUp, + hasRemove, + index, + onDropIndexClick, + onReorderClick, + readonly, + uiSchema, + registry, + } = props; + const { MoveDownButton, MoveUpButton, RemoveButton } = + registry.templates.ButtonTemplates; + const btnStyle: CSSProperties = { + flex: 1, + paddingLeft: 6, + paddingRight: 6, + fontWeight: 'bold', + minWidth: 0, + }; + return ( + + + + + {children} + + + + {hasToolbar && ( + + {(hasMoveUp || hasMoveDown) && ( + + )} + {(hasMoveUp || hasMoveDown) && ( + + )} + {hasRemove && ( + + )} + + )} + + ); +} + +export default ArrayFieldItemTemplate; diff --git a/spiffworkflow-frontend/src/themes/carbon/ArrayFieldItemTemplate/index.ts b/spiffworkflow-frontend/src/themes/carbon/ArrayFieldItemTemplate/index.ts new file mode 100644 index 00000000..f1044313 --- /dev/null +++ b/spiffworkflow-frontend/src/themes/carbon/ArrayFieldItemTemplate/index.ts @@ -0,0 +1,2 @@ +export { default } from './ArrayFieldItemTemplate'; +export * from './ArrayFieldItemTemplate'; diff --git a/spiffworkflow-frontend/src/themes/carbon/ArrayFieldTemplate/ArrayFieldTemplate.tsx b/spiffworkflow-frontend/src/themes/carbon/ArrayFieldTemplate/ArrayFieldTemplate.tsx new file mode 100644 index 00000000..03c88951 --- /dev/null +++ b/spiffworkflow-frontend/src/themes/carbon/ArrayFieldTemplate/ArrayFieldTemplate.tsx @@ -0,0 +1,90 @@ +import React from 'react'; +import Box from '@mui/material/Box'; +import Grid from '@mui/material/Grid'; +import Paper from '@mui/material/Paper'; +import { + ArrayFieldTemplateItemType, + ArrayFieldTemplateProps, + getTemplate, + getUiOptions, +} from '@rjsf/utils'; + +function ArrayFieldTemplate(props: ArrayFieldTemplateProps) { + const { + canAdd, + disabled, + idSchema, + uiSchema, + items, + onAddClick, + readonly, + registry, + required, + schema, + title, + } = props; + const uiOptions = getUiOptions(uiSchema); + const ArrayFieldDescriptionTemplate = + getTemplate<'ArrayFieldDescriptionTemplate'>( + 'ArrayFieldDescriptionTemplate', + registry, + uiOptions + ); + const ArrayFieldItemTemplate = getTemplate<'ArrayFieldItemTemplate'>( + 'ArrayFieldItemTemplate', + registry, + uiOptions + ); + const ArrayFieldTitleTemplate = getTemplate<'ArrayFieldTitleTemplate'>( + 'ArrayFieldTitleTemplate', + registry, + uiOptions + ); + // Button templates are not overridden in the uiSchema + const { + ButtonTemplates: { AddButton }, + } = registry.templates; + return ( + + + + + + {items && + items.map(({ key, ...itemProps }: ArrayFieldTemplateItemType) => ( + + ))} + {canAdd && ( + + + + + + + + )} + + + + ); +} + +export default ArrayFieldTemplate; diff --git a/spiffworkflow-frontend/src/themes/carbon/ArrayFieldTemplate/index.ts b/spiffworkflow-frontend/src/themes/carbon/ArrayFieldTemplate/index.ts new file mode 100644 index 00000000..ab908dec --- /dev/null +++ b/spiffworkflow-frontend/src/themes/carbon/ArrayFieldTemplate/index.ts @@ -0,0 +1,2 @@ +export { default } from './ArrayFieldTemplate'; +export * from './ArrayFieldTemplate'; diff --git a/spiffworkflow-frontend/src/themes/carbon/BaseInputTemplate/BaseInputTemplate.tsx b/spiffworkflow-frontend/src/themes/carbon/BaseInputTemplate/BaseInputTemplate.tsx new file mode 100644 index 00000000..1a9ad5a1 --- /dev/null +++ b/spiffworkflow-frontend/src/themes/carbon/BaseInputTemplate/BaseInputTemplate.tsx @@ -0,0 +1,82 @@ +import React from 'react'; +import TextField, { TextFieldProps } from '@mui/material/TextField'; +import { getInputProps, WidgetProps } from '@rjsf/utils'; + +function BaseInputTemplate({ + id, + placeholder, + required, + readonly, + disabled, + type, + label, + value, + onChange, + onBlur, + onFocus, + autofocus, + options, + schema, + uiSchema, + rawErrors = [], + formContext, + registry, + ...textFieldProps +}: WidgetProps) { + const inputProps = getInputProps(schema, type, options); + // Now we need to pull out the step, min, max into an inner `inputProps` for material-ui + const { step, min, max, ...rest } = inputProps; + const otherProps = { + inputProps: { + step, + min, + max, + ...(schema.examples ? { list: `examples_${id}` } : undefined), + }, + ...rest, + }; + const _onChange = ({ + target: { value }, + }: React.ChangeEvent) => + onChange(value === '' ? options.emptyValue : value); + const _onBlur = ({ target: { value } }: React.FocusEvent) => + onBlur(id, value); + const _onFocus = ({ + target: { value }, + }: React.FocusEvent) => onFocus(id, value); + + const { schemaUtils } = registry; + const displayLabel = schemaUtils.getDisplayLabel(schema, uiSchema); + + return ( + <> + 0} + onChange={_onChange} + onBlur={_onBlur} + onFocus={_onFocus} + {...(textFieldProps as TextFieldProps)} + /> + {schema.examples && ( + + {(schema.examples as string[]) + .concat(schema.default ? ([schema.default] as string[]) : []) + .map((example: any) => { + return + )} + + ); +} + +export default BaseInputTemplate; diff --git a/spiffworkflow-frontend/src/themes/carbon/BaseInputTemplate/index.ts b/spiffworkflow-frontend/src/themes/carbon/BaseInputTemplate/index.ts new file mode 100644 index 00000000..f7ef8d59 --- /dev/null +++ b/spiffworkflow-frontend/src/themes/carbon/BaseInputTemplate/index.ts @@ -0,0 +1,2 @@ +export { default } from './BaseInputTemplate'; +export * from './BaseInputTemplate'; diff --git a/spiffworkflow-frontend/src/themes/carbon/CheckboxWidget/CheckboxWidget.tsx b/spiffworkflow-frontend/src/themes/carbon/CheckboxWidget/CheckboxWidget.tsx new file mode 100644 index 00000000..6d2303a6 --- /dev/null +++ b/spiffworkflow-frontend/src/themes/carbon/CheckboxWidget/CheckboxWidget.tsx @@ -0,0 +1,52 @@ +import React from 'react'; +import Checkbox from '@mui/material/Checkbox'; +import FormControlLabel from '@mui/material/FormControlLabel'; +import { schemaRequiresTrueValue, WidgetProps } from '@rjsf/utils'; + +function CheckboxWidget(props: WidgetProps) { + const { + schema, + id, + value, + disabled, + readonly, + label, + autofocus, + onChange, + onBlur, + onFocus, + } = props; + // Because an unchecked checkbox will cause html5 validation to fail, only add + // the "required" attribute if the field value must be "true", due to the + // "const" or "enum" keywords + const required = schemaRequiresTrueValue(schema); + + const _onChange = (_: any, checked: boolean) => onChange(checked); + const _onBlur = ({ + target: { value }, + }: React.FocusEvent) => onBlur(id, value); + const _onFocus = ({ + target: { value }, + }: React.FocusEvent) => onFocus(id, value); + + return ( + + } + label={label || ''} + /> + ); +} + +export default CheckboxWidget; diff --git a/spiffworkflow-frontend/src/themes/carbon/CheckboxWidget/index.ts b/spiffworkflow-frontend/src/themes/carbon/CheckboxWidget/index.ts new file mode 100644 index 00000000..b9e3c318 --- /dev/null +++ b/spiffworkflow-frontend/src/themes/carbon/CheckboxWidget/index.ts @@ -0,0 +1,2 @@ +export { default } from './CheckboxWidget'; +export * from './CheckboxWidget'; diff --git a/spiffworkflow-frontend/src/themes/carbon/CheckboxesWidget/CheckboxesWidget.tsx b/spiffworkflow-frontend/src/themes/carbon/CheckboxesWidget/CheckboxesWidget.tsx new file mode 100644 index 00000000..6d821cdb --- /dev/null +++ b/spiffworkflow-frontend/src/themes/carbon/CheckboxesWidget/CheckboxesWidget.tsx @@ -0,0 +1,93 @@ +import React from 'react'; +import Checkbox from '@mui/material/Checkbox'; +import FormControlLabel from '@mui/material/FormControlLabel'; +import FormGroup from '@mui/material/FormGroup'; +import FormLabel from '@mui/material/FormLabel'; +import { WidgetProps } from '@rjsf/utils'; + +const selectValue = (value: any, selected: any, all: any) => { + const at = all.indexOf(value); + const updated = selected.slice(0, at).concat(value, selected.slice(at)); + + // As inserting values at predefined index positions doesn't work with empty + // arrays, we need to reorder the updated selection to match the initial order + return updated.sort((a: any, b: any) => all.indexOf(a) > all.indexOf(b)); +}; + +const deselectValue = (value: any, selected: any) => { + return selected.filter((v: any) => v !== value); +}; + +function CheckboxesWidget({ + schema, + label, + id, + disabled, + options, + value, + autofocus, + readonly, + required, + onChange, + onBlur, + onFocus, +}: WidgetProps) { + const { enumOptions, enumDisabled, inline } = options; + + const _onChange = + (option: any) => + ({ target: { checked } }: React.ChangeEvent) => { + const all = (enumOptions as any).map(({ value }: any) => value); + + if (checked) { + onChange(selectValue(option.value, value, all)); + } else { + onChange(deselectValue(option.value, value)); + } + }; + + const _onBlur = ({ + target: { value }, + }: React.FocusEvent) => onBlur(id, value); + const _onFocus = ({ + target: { value }, + }: React.FocusEvent) => onFocus(id, value); + + return ( + <> + + {label || schema.title} + + + {Array.isArray(enumOptions) && + enumOptions.map((option, index: number) => { + const checked = value.indexOf(option.value) !== -1; + const itemDisabled = + Array.isArray(enumDisabled) && + enumDisabled.indexOf(option.value) !== -1; + const checkbox = ( + + ); + return ( + + ); + })} + + + ); +} + +export default CheckboxesWidget; diff --git a/spiffworkflow-frontend/src/themes/carbon/CheckboxesWidget/index.ts b/spiffworkflow-frontend/src/themes/carbon/CheckboxesWidget/index.ts new file mode 100644 index 00000000..97152004 --- /dev/null +++ b/spiffworkflow-frontend/src/themes/carbon/CheckboxesWidget/index.ts @@ -0,0 +1,2 @@ +export { default } from './CheckboxesWidget'; +export * from './CheckboxesWidget'; diff --git a/spiffworkflow-frontend/src/themes/carbon/DateTimeWidget/DateTimeWidget.tsx b/spiffworkflow-frontend/src/themes/carbon/DateTimeWidget/DateTimeWidget.tsx new file mode 100644 index 00000000..df26f459 --- /dev/null +++ b/spiffworkflow-frontend/src/themes/carbon/DateTimeWidget/DateTimeWidget.tsx @@ -0,0 +1,29 @@ +import React from 'react'; +import { getTemplate, localToUTC, utcToLocal, WidgetProps } from '@rjsf/utils'; + +function DateTimeWidget(props: WidgetProps) { + const { options, registry } = props; + const BaseInputTemplate = getTemplate<'BaseInputTemplate'>( + 'BaseInputTemplate', + registry, + options + ); + const value = utcToLocal(props.value); + const onChange = (value: any) => { + props.onChange(localToUTC(value)); + }; + + return ( + + ); +} + +export default DateTimeWidget; diff --git a/spiffworkflow-frontend/src/themes/carbon/DateTimeWidget/index.ts b/spiffworkflow-frontend/src/themes/carbon/DateTimeWidget/index.ts new file mode 100644 index 00000000..0db36616 --- /dev/null +++ b/spiffworkflow-frontend/src/themes/carbon/DateTimeWidget/index.ts @@ -0,0 +1,2 @@ +export { default } from './DateTimeWidget'; +export * from './DateTimeWidget'; diff --git a/spiffworkflow-frontend/src/themes/carbon/DateWidget/DateWidget.tsx b/spiffworkflow-frontend/src/themes/carbon/DateWidget/DateWidget.tsx new file mode 100644 index 00000000..fcf7cfcf --- /dev/null +++ b/spiffworkflow-frontend/src/themes/carbon/DateWidget/DateWidget.tsx @@ -0,0 +1,22 @@ +import React from 'react'; +import { getTemplate, WidgetProps } from '@rjsf/utils'; + +function DateWidget(props: WidgetProps) { + const { options, registry } = props; + const BaseInputTemplate = getTemplate<'BaseInputTemplate'>( + 'BaseInputTemplate', + registry, + options + ); + return ( + + ); +} + +export default DateWidget; diff --git a/spiffworkflow-frontend/src/themes/carbon/DateWidget/index.ts b/spiffworkflow-frontend/src/themes/carbon/DateWidget/index.ts new file mode 100644 index 00000000..923b0077 --- /dev/null +++ b/spiffworkflow-frontend/src/themes/carbon/DateWidget/index.ts @@ -0,0 +1,2 @@ +export { default } from './DateWidget'; +export * from './DateWidget'; diff --git a/spiffworkflow-frontend/src/themes/carbon/DescriptionField/DescriptionField.tsx b/spiffworkflow-frontend/src/themes/carbon/DescriptionField/DescriptionField.tsx new file mode 100644 index 00000000..a5465764 --- /dev/null +++ b/spiffworkflow-frontend/src/themes/carbon/DescriptionField/DescriptionField.tsx @@ -0,0 +1,17 @@ +import React from 'react'; +import Typography from '@mui/material/Typography'; +import { DescriptionFieldProps } from '@rjsf/utils'; + +function DescriptionField({ description, id }: DescriptionFieldProps) { + if (description) { + return ( + + {description} + + ); + } + + return null; +} + +export default DescriptionField; diff --git a/spiffworkflow-frontend/src/themes/carbon/DescriptionField/index.ts b/spiffworkflow-frontend/src/themes/carbon/DescriptionField/index.ts new file mode 100644 index 00000000..401540d9 --- /dev/null +++ b/spiffworkflow-frontend/src/themes/carbon/DescriptionField/index.ts @@ -0,0 +1,2 @@ +export { default } from './DescriptionField'; +export * from './DescriptionField'; diff --git a/spiffworkflow-frontend/src/themes/carbon/ErrorList/ErrorList.tsx b/spiffworkflow-frontend/src/themes/carbon/ErrorList/ErrorList.tsx new file mode 100644 index 00000000..95f41076 --- /dev/null +++ b/spiffworkflow-frontend/src/themes/carbon/ErrorList/ErrorList.tsx @@ -0,0 +1,34 @@ +import React from 'react'; +import ErrorIcon from '@mui/icons-material/Error'; +import Box from '@mui/material/Box'; +import List from '@mui/material/List'; +import ListItem from '@mui/material/ListItem'; +import ListItemIcon from '@mui/material/ListItemIcon'; +import ListItemText from '@mui/material/ListItemText'; +import Paper from '@mui/material/Paper'; +import Typography from '@mui/material/Typography'; +import { ErrorListProps } from '@rjsf/utils'; + +function ErrorList({ errors }: ErrorListProps) { + return ( + + + Errors + + {errors.map((error, i: number) => { + return ( + + + + + + + ); + })} + + + + ); +} + +export default ErrorList; diff --git a/spiffworkflow-frontend/src/themes/carbon/ErrorList/index.ts b/spiffworkflow-frontend/src/themes/carbon/ErrorList/index.ts new file mode 100644 index 00000000..79376ace --- /dev/null +++ b/spiffworkflow-frontend/src/themes/carbon/ErrorList/index.ts @@ -0,0 +1,2 @@ +export { default } from './ErrorList'; +export * from './ErrorList'; diff --git a/spiffworkflow-frontend/src/themes/carbon/FieldErrorTemplate/FieldErrorTemplate.tsx b/spiffworkflow-frontend/src/themes/carbon/FieldErrorTemplate/FieldErrorTemplate.tsx new file mode 100644 index 00000000..46f244ba --- /dev/null +++ b/spiffworkflow-frontend/src/themes/carbon/FieldErrorTemplate/FieldErrorTemplate.tsx @@ -0,0 +1,29 @@ +import React from 'react'; +import { FieldErrorProps } from '@rjsf/utils'; +import ListItem from '@mui/material/ListItem'; +import FormHelperText from '@mui/material/FormHelperText'; +import List from '@mui/material/List'; + +/** The `FieldErrorTemplate` component renders the errors local to the particular field + * + * @param props - The `FieldErrorProps` for the errors being rendered + */ +export default function FieldErrorTemplate(props: FieldErrorProps) { + const { errors = [], idSchema } = props; + if (errors.length === 0) { + return null; + } + const id = `${idSchema.$id}__error`; + + return ( + + {errors.map((error, i: number) => { + return ( + + {error} + + ); + })} + + ); +} diff --git a/spiffworkflow-frontend/src/themes/carbon/FieldErrorTemplate/index.ts b/spiffworkflow-frontend/src/themes/carbon/FieldErrorTemplate/index.ts new file mode 100644 index 00000000..2fbf1c35 --- /dev/null +++ b/spiffworkflow-frontend/src/themes/carbon/FieldErrorTemplate/index.ts @@ -0,0 +1,2 @@ +export { default } from './FieldErrorTemplate'; +export * from './FieldErrorTemplate'; diff --git a/spiffworkflow-frontend/src/themes/carbon/FieldHelpTemplate/FieldHelpTemplate.tsx b/spiffworkflow-frontend/src/themes/carbon/FieldHelpTemplate/FieldHelpTemplate.tsx new file mode 100644 index 00000000..08a61aeb --- /dev/null +++ b/spiffworkflow-frontend/src/themes/carbon/FieldHelpTemplate/FieldHelpTemplate.tsx @@ -0,0 +1,16 @@ +import React from 'react'; +import { FieldHelpProps } from '@rjsf/utils'; +import FormHelperText from '@mui/material/FormHelperText'; + +/** The `FieldHelpTemplate` component renders any help desired for a field + * + * @param props - The `FieldHelpProps` to be rendered + */ +export default function FieldHelpTemplate(props: FieldHelpProps) { + const { idSchema, help } = props; + if (!help) { + return null; + } + const id = `${idSchema.$id}__help`; + return {help}; +} diff --git a/spiffworkflow-frontend/src/themes/carbon/FieldHelpTemplate/index.ts b/spiffworkflow-frontend/src/themes/carbon/FieldHelpTemplate/index.ts new file mode 100644 index 00000000..b439bce3 --- /dev/null +++ b/spiffworkflow-frontend/src/themes/carbon/FieldHelpTemplate/index.ts @@ -0,0 +1,2 @@ +export { default } from './FieldHelpTemplate'; +export * from './FieldHelpTemplate'; diff --git a/spiffworkflow-frontend/src/themes/carbon/FieldTemplate/FieldTemplate.tsx b/spiffworkflow-frontend/src/themes/carbon/FieldTemplate/FieldTemplate.tsx new file mode 100644 index 00000000..44b687ea --- /dev/null +++ b/spiffworkflow-frontend/src/themes/carbon/FieldTemplate/FieldTemplate.tsx @@ -0,0 +1,64 @@ +import React from 'react'; +import FormControl from '@mui/material/FormControl'; +import Typography from '@mui/material/Typography'; +import { FieldTemplateProps, getTemplate, getUiOptions } from '@rjsf/utils'; + +function FieldTemplate({ + id, + children, + classNames, + disabled, + displayLabel, + hidden, + label, + onDropPropertyClick, + onKeyChange, + readonly, + required, + rawErrors = [], + errors, + help, + rawDescription, + schema, + uiSchema, + registry, +}: FieldTemplateProps) { + const uiOptions = getUiOptions(uiSchema); + const WrapIfAdditionalTemplate = getTemplate<'WrapIfAdditionalTemplate'>( + 'WrapIfAdditionalTemplate', + registry, + uiOptions + ); + + if (hidden) { + return
{children}
; + } + return ( + + + {children} + {displayLabel && rawDescription ? ( + + {rawDescription} + + ) : null} + {errors} + {help} + + + ); +} + +export default FieldTemplate; diff --git a/spiffworkflow-frontend/src/themes/carbon/FieldTemplate/index.ts b/spiffworkflow-frontend/src/themes/carbon/FieldTemplate/index.ts new file mode 100644 index 00000000..6f7dc386 --- /dev/null +++ b/spiffworkflow-frontend/src/themes/carbon/FieldTemplate/index.ts @@ -0,0 +1,2 @@ +export { default } from './FieldTemplate'; +export * from './FieldTemplate'; diff --git a/spiffworkflow-frontend/src/themes/carbon/IconButton/IconButton.tsx b/spiffworkflow-frontend/src/themes/carbon/IconButton/IconButton.tsx new file mode 100644 index 00000000..82559106 --- /dev/null +++ b/spiffworkflow-frontend/src/themes/carbon/IconButton/IconButton.tsx @@ -0,0 +1,55 @@ +import React from 'react'; +import IconButton, { + IconButtonProps as MuiIconButtonProps, +} from '@mui/material/IconButton'; +import ArrowDownwardIcon from '@mui/icons-material/ArrowDownward'; +import ArrowUpwardIcon from '@mui/icons-material/ArrowUpward'; +import RemoveIcon from '@mui/icons-material/Remove'; +import { IconButtonProps } from '@rjsf/utils'; + +export default function MuiIconButton(props: IconButtonProps) { + const { icon, color, uiSchema, ...otherProps } = props; + return ( + + {icon} + + ); +} + +export function MoveDownButton(props: IconButtonProps) { + return ( + } + /> + ); +} + +export function MoveUpButton(props: IconButtonProps) { + return ( + } + /> + ); +} + +export function RemoveButton(props: IconButtonProps) { + const { iconType, ...otherProps } = props; + return ( + + } + /> + ); +} diff --git a/spiffworkflow-frontend/src/themes/carbon/IconButton/index.ts b/spiffworkflow-frontend/src/themes/carbon/IconButton/index.ts new file mode 100644 index 00000000..655ec4c4 --- /dev/null +++ b/spiffworkflow-frontend/src/themes/carbon/IconButton/index.ts @@ -0,0 +1,2 @@ +export { default } from './IconButton'; +export * from './IconButton'; diff --git a/spiffworkflow-frontend/src/themes/carbon/MuiForm/MuiForm.tsx b/spiffworkflow-frontend/src/themes/carbon/MuiForm/MuiForm.tsx new file mode 100644 index 00000000..c57e97e7 --- /dev/null +++ b/spiffworkflow-frontend/src/themes/carbon/MuiForm/MuiForm.tsx @@ -0,0 +1,8 @@ +import { ComponentType } from 'react'; +import { withTheme, FormProps } from '@rjsf/core'; + +import Theme from '../Theme'; + +const MuiForm: ComponentType = withTheme(Theme); + +export default MuiForm; diff --git a/spiffworkflow-frontend/src/themes/carbon/MuiForm/index.ts b/spiffworkflow-frontend/src/themes/carbon/MuiForm/index.ts new file mode 100644 index 00000000..f4609de6 --- /dev/null +++ b/spiffworkflow-frontend/src/themes/carbon/MuiForm/index.ts @@ -0,0 +1,2 @@ +export { default } from './MuiForm'; +export * from './MuiForm'; diff --git a/spiffworkflow-frontend/src/themes/carbon/ObjectFieldTemplate/ObjectFieldTemplate.tsx b/spiffworkflow-frontend/src/themes/carbon/ObjectFieldTemplate/ObjectFieldTemplate.tsx new file mode 100644 index 00000000..7d9557e8 --- /dev/null +++ b/spiffworkflow-frontend/src/themes/carbon/ObjectFieldTemplate/ObjectFieldTemplate.tsx @@ -0,0 +1,89 @@ +import React from 'react'; +import Grid from '@mui/material/Grid'; +import { + ObjectFieldTemplateProps, + canExpand, + getTemplate, + getUiOptions, +} from '@rjsf/utils'; + +function ObjectFieldTemplate({ + description, + title, + properties, + required, + disabled, + readonly, + uiSchema, + idSchema, + schema, + formData, + onAddClick, + registry, +}: ObjectFieldTemplateProps) { + const uiOptions = getUiOptions(uiSchema); + const TitleFieldTemplate = getTemplate<'TitleFieldTemplate'>( + 'TitleFieldTemplate', + registry, + uiOptions + ); + const DescriptionFieldTemplate = getTemplate<'DescriptionFieldTemplate'>( + 'DescriptionFieldTemplate', + registry, + uiOptions + ); + // Button templates are not overridden in the uiSchema + const { + ButtonTemplates: { AddButton }, + } = registry.templates; + return ( + <> + {(uiOptions.title || title) && ( + + )} + {(uiOptions.description || description) && ( + + )} + + {properties.map((element, index) => + // Remove the if the inner element is hidden as the + // itself would otherwise still take up space. + element.hidden ? ( + element.content + ) : ( + + {element.content} + + ) + )} + {canExpand(schema, uiSchema, formData) && ( + + + + + + )} + + + ); +} + +export default ObjectFieldTemplate; diff --git a/spiffworkflow-frontend/src/themes/carbon/ObjectFieldTemplate/index.ts b/spiffworkflow-frontend/src/themes/carbon/ObjectFieldTemplate/index.ts new file mode 100644 index 00000000..77c68a9a --- /dev/null +++ b/spiffworkflow-frontend/src/themes/carbon/ObjectFieldTemplate/index.ts @@ -0,0 +1,2 @@ +export { default } from './ObjectFieldTemplate'; +export * from './ObjectFieldTemplate'; diff --git a/spiffworkflow-frontend/src/themes/carbon/RadioWidget/RadioWidget.tsx b/spiffworkflow-frontend/src/themes/carbon/RadioWidget/RadioWidget.tsx new file mode 100644 index 00000000..de22cf1d --- /dev/null +++ b/spiffworkflow-frontend/src/themes/carbon/RadioWidget/RadioWidget.tsx @@ -0,0 +1,73 @@ +import React from 'react'; +import FormControlLabel from '@mui/material/FormControlLabel'; +import FormLabel from '@mui/material/FormLabel'; +import Radio from '@mui/material/Radio'; +import RadioGroup from '@mui/material/RadioGroup'; +import { WidgetProps } from '@rjsf/utils'; + +function RadioWidget({ + id, + schema, + options, + value, + required, + disabled, + readonly, + label, + onChange, + onBlur, + onFocus, +}: WidgetProps) { + const { enumOptions, enumDisabled } = options; + + const _onChange = (_: any, value: any) => + onChange(schema.type == 'boolean' ? value !== 'false' : value); + const _onBlur = ({ target: { value } }: React.FocusEvent) => + onBlur(id, value); + const _onFocus = ({ + target: { value }, + }: React.FocusEvent) => onFocus(id, value); + + const row = options ? options.inline : false; + + return ( + <> + + {label || schema.title} + + + {Array.isArray(enumOptions) && + enumOptions.map((option) => { + const itemDisabled = + Array.isArray(enumDisabled) && + enumDisabled.indexOf(option.value) !== -1; + return ( + + } + label={`${option.label}`} + value={`${option.value}`} + key={option.value} + disabled={disabled || itemDisabled || readonly} + /> + ); + })} + + + ); +} + +export default RadioWidget; diff --git a/spiffworkflow-frontend/src/themes/carbon/RadioWidget/index.ts b/spiffworkflow-frontend/src/themes/carbon/RadioWidget/index.ts new file mode 100644 index 00000000..10292dc5 --- /dev/null +++ b/spiffworkflow-frontend/src/themes/carbon/RadioWidget/index.ts @@ -0,0 +1,2 @@ +export { default } from './RadioWidget'; +export * from './RadioWidget'; diff --git a/spiffworkflow-frontend/src/themes/carbon/RangeWidget/RangeWidget.tsx b/spiffworkflow-frontend/src/themes/carbon/RangeWidget/RangeWidget.tsx new file mode 100644 index 00000000..a00d4a02 --- /dev/null +++ b/spiffworkflow-frontend/src/themes/carbon/RangeWidget/RangeWidget.tsx @@ -0,0 +1,47 @@ +import React from 'react'; +import FormLabel from '@mui/material/FormLabel'; +import Slider from '@mui/material/Slider'; +import { WidgetProps, rangeSpec } from '@rjsf/utils'; + +function RangeWidget({ + value, + readonly, + disabled, + onBlur, + onFocus, + options, + schema, + onChange, + required, + label, + id, +}: WidgetProps) { + const sliderProps = { value, label, id, name: id, ...rangeSpec(schema) }; + + const _onChange = (_: any, value?: number | number[]) => { + onChange(value ? options.emptyValue : value); + }; + const _onBlur = ({ target: { value } }: React.FocusEvent) => + onBlur(id, value); + const _onFocus = ({ + target: { value }, + }: React.FocusEvent) => onFocus(id, value); + + return ( + <> + + {label} + + + + ); +} + +export default RangeWidget; diff --git a/spiffworkflow-frontend/src/themes/carbon/RangeWidget/index.ts b/spiffworkflow-frontend/src/themes/carbon/RangeWidget/index.ts new file mode 100644 index 00000000..d8c49226 --- /dev/null +++ b/spiffworkflow-frontend/src/themes/carbon/RangeWidget/index.ts @@ -0,0 +1,2 @@ +export { default } from './RangeWidget'; +export * from './RangeWidget'; diff --git a/spiffworkflow-frontend/src/themes/carbon/SelectWidget/SelectWidget.tsx b/spiffworkflow-frontend/src/themes/carbon/SelectWidget/SelectWidget.tsx new file mode 100644 index 00000000..0aadcbe3 --- /dev/null +++ b/spiffworkflow-frontend/src/themes/carbon/SelectWidget/SelectWidget.tsx @@ -0,0 +1,71 @@ +import React from 'react'; +import MenuItem from '@mui/material/MenuItem'; +import TextField from '@mui/material/TextField'; +import { WidgetProps, processSelectValue } from '@rjsf/utils'; + +function SelectWidget({ + schema, + id, + options, + label, + required, + disabled, + readonly, + value, + multiple, + autofocus, + onChange, + onBlur, + onFocus, + rawErrors = [], +}: WidgetProps) { + const { enumOptions, enumDisabled } = options; + + const emptyValue = multiple ? [] : ''; + + const _onChange = ({ + target: { value }, + }: React.ChangeEvent<{ name?: string; value: unknown }>) => + onChange(processSelectValue(schema, value, options)); + const _onBlur = ({ target: { value } }: React.FocusEvent) => + onBlur(id, processSelectValue(schema, value, options)); + const _onFocus = ({ + target: { value }, + }: React.FocusEvent) => + onFocus(id, processSelectValue(schema, value, options)); + + return ( + 0} + onChange={_onChange} + onBlur={_onBlur} + onFocus={_onFocus} + InputLabelProps={{ + shrink: true, + }} + SelectProps={{ + multiple: typeof multiple === 'undefined' ? false : multiple, + }} + > + {(enumOptions as any).map(({ value, label }: any, i: number) => { + const disabled: any = + enumDisabled && (enumDisabled as any).indexOf(value) != -1; + return ( + + {label} + + ); + })} + + ); +} + +export default SelectWidget; diff --git a/spiffworkflow-frontend/src/themes/carbon/SelectWidget/index.ts b/spiffworkflow-frontend/src/themes/carbon/SelectWidget/index.ts new file mode 100644 index 00000000..e37ea725 --- /dev/null +++ b/spiffworkflow-frontend/src/themes/carbon/SelectWidget/index.ts @@ -0,0 +1,2 @@ +export { default } from './SelectWidget'; +export * from './SelectWidget'; diff --git a/spiffworkflow-frontend/src/themes/carbon/SubmitButton/SubmitButton.tsx b/spiffworkflow-frontend/src/themes/carbon/SubmitButton/SubmitButton.tsx new file mode 100644 index 00000000..b61d30c8 --- /dev/null +++ b/spiffworkflow-frontend/src/themes/carbon/SubmitButton/SubmitButton.tsx @@ -0,0 +1,29 @@ +import React from 'react'; +import Box from '@mui/material/Box'; +import Button from '@mui/material/Button'; +import { SubmitButtonProps, getSubmitButtonOptions } from '@rjsf/utils'; + +const SubmitButton: React.ComponentType = (props) => { + const { + submitText, + norender, + props: submitButtonProps, + } = getSubmitButtonOptions(props.uiSchema); + if (norender) { + return null; + } + return ( + + + + ); +}; + +export default SubmitButton; diff --git a/spiffworkflow-frontend/src/themes/carbon/SubmitButton/index.ts b/spiffworkflow-frontend/src/themes/carbon/SubmitButton/index.ts new file mode 100644 index 00000000..f676497b --- /dev/null +++ b/spiffworkflow-frontend/src/themes/carbon/SubmitButton/index.ts @@ -0,0 +1,2 @@ +export { default } from './SubmitButton'; +export * from './SubmitButton'; diff --git a/spiffworkflow-frontend/src/themes/carbon/Templates/Templates.ts b/spiffworkflow-frontend/src/themes/carbon/Templates/Templates.ts new file mode 100644 index 00000000..dfa9229f --- /dev/null +++ b/spiffworkflow-frontend/src/themes/carbon/Templates/Templates.ts @@ -0,0 +1,35 @@ +import AddButton from '../AddButton'; +import ArrayFieldItemTemplate from '../ArrayFieldItemTemplate'; +import ArrayFieldTemplate from '../ArrayFieldTemplate'; +import BaseInputTemplate from '../BaseInputTemplate'; +import DescriptionField from '../DescriptionField'; +import ErrorList from '../ErrorList'; +import { MoveDownButton, MoveUpButton, RemoveButton } from '../IconButton'; +import FieldErrorTemplate from '../FieldErrorTemplate'; +import FieldHelpTemplate from '../FieldHelpTemplate'; +import FieldTemplate from '../FieldTemplate'; +import ObjectFieldTemplate from '../ObjectFieldTemplate'; +import SubmitButton from '../SubmitButton'; +import TitleField from '../TitleField'; +import WrapIfAdditionalTemplate from '../WrapIfAdditionalTemplate'; + +export default { + ArrayFieldItemTemplate, + ArrayFieldTemplate, + BaseInputTemplate, + ButtonTemplates: { + AddButton, + MoveDownButton, + MoveUpButton, + RemoveButton, + SubmitButton, + }, + DescriptionFieldTemplate: DescriptionField, + ErrorListTemplate: ErrorList, + FieldErrorTemplate, + FieldHelpTemplate, + FieldTemplate, + ObjectFieldTemplate, + TitleFieldTemplate: TitleField, + WrapIfAdditionalTemplate, +}; diff --git a/spiffworkflow-frontend/src/themes/carbon/Templates/index.ts b/spiffworkflow-frontend/src/themes/carbon/Templates/index.ts new file mode 100644 index 00000000..612ccf69 --- /dev/null +++ b/spiffworkflow-frontend/src/themes/carbon/Templates/index.ts @@ -0,0 +1,2 @@ +export { default } from './Templates'; +export * from './Templates'; diff --git a/spiffworkflow-frontend/src/themes/carbon/TextareaWidget/TextareaWidget.tsx b/spiffworkflow-frontend/src/themes/carbon/TextareaWidget/TextareaWidget.tsx new file mode 100644 index 00000000..79e91750 --- /dev/null +++ b/spiffworkflow-frontend/src/themes/carbon/TextareaWidget/TextareaWidget.tsx @@ -0,0 +1,20 @@ +import React from 'react'; +import { getTemplate, WidgetProps } from '@rjsf/utils'; + +function TextareaWidget(props: WidgetProps) { + const { options, registry } = props; + const BaseInputTemplate = getTemplate<'BaseInputTemplate'>( + 'BaseInputTemplate', + registry, + options + ); + + let rows: string | number = 5; + if (typeof options.rows === 'string' || typeof options.rows === 'number') { + rows = options.rows; + } + + return ; +} + +export default TextareaWidget; diff --git a/spiffworkflow-frontend/src/themes/carbon/TextareaWidget/index.ts b/spiffworkflow-frontend/src/themes/carbon/TextareaWidget/index.ts new file mode 100644 index 00000000..20e6d8e2 --- /dev/null +++ b/spiffworkflow-frontend/src/themes/carbon/TextareaWidget/index.ts @@ -0,0 +1,2 @@ +export { default } from './TextareaWidget'; +export * from './TextareaWidget'; diff --git a/spiffworkflow-frontend/src/themes/carbon/Theme/Theme.tsx b/spiffworkflow-frontend/src/themes/carbon/Theme/Theme.tsx new file mode 100644 index 00000000..ee71932b --- /dev/null +++ b/spiffworkflow-frontend/src/themes/carbon/Theme/Theme.tsx @@ -0,0 +1,11 @@ +import { ThemeProps } from '@rjsf/core'; + +import Templates from '../Templates'; +import Widgets from '../Widgets'; + +const Theme: ThemeProps = { + templates: Templates, + widgets: Widgets, +}; + +export default Theme; diff --git a/spiffworkflow-frontend/src/themes/carbon/Theme/index.ts b/spiffworkflow-frontend/src/themes/carbon/Theme/index.ts new file mode 100644 index 00000000..6dfd7fa6 --- /dev/null +++ b/spiffworkflow-frontend/src/themes/carbon/Theme/index.ts @@ -0,0 +1,2 @@ +export { default } from './Theme'; +export * from './Theme'; diff --git a/spiffworkflow-frontend/src/themes/carbon/TitleField/TitleField.tsx b/spiffworkflow-frontend/src/themes/carbon/TitleField/TitleField.tsx new file mode 100644 index 00000000..8f0d3ae0 --- /dev/null +++ b/spiffworkflow-frontend/src/themes/carbon/TitleField/TitleField.tsx @@ -0,0 +1,16 @@ +import React from 'react'; +import Box from '@mui/material/Box'; +import Divider from '@mui/material/Divider'; +import Typography from '@mui/material/Typography'; +import { TitleFieldProps } from '@rjsf/utils'; + +function TitleField({ id, title }: TitleFieldProps) { + return ( + + {title} + + + ); +} + +export default TitleField; diff --git a/spiffworkflow-frontend/src/themes/carbon/TitleField/index.ts b/spiffworkflow-frontend/src/themes/carbon/TitleField/index.ts new file mode 100644 index 00000000..cfa479d0 --- /dev/null +++ b/spiffworkflow-frontend/src/themes/carbon/TitleField/index.ts @@ -0,0 +1,2 @@ +export { default } from './TitleField'; +export * from './TitleField'; diff --git a/spiffworkflow-frontend/src/themes/carbon/Widgets/Widgets.ts b/spiffworkflow-frontend/src/themes/carbon/Widgets/Widgets.ts new file mode 100644 index 00000000..6c34611d --- /dev/null +++ b/spiffworkflow-frontend/src/themes/carbon/Widgets/Widgets.ts @@ -0,0 +1,19 @@ +import CheckboxWidget from '../CheckboxWidget/CheckboxWidget'; +import CheckboxesWidget from '../CheckboxesWidget/CheckboxesWidget'; +import DateWidget from '../DateWidget/DateWidget'; +import DateTimeWidget from '../DateTimeWidget/DateTimeWidget'; +import RadioWidget from '../RadioWidget/RadioWidget'; +import RangeWidget from '../RangeWidget/RangeWidget'; +import SelectWidget from '../SelectWidget/SelectWidget'; +import TextareaWidget from '../TextareaWidget/TextareaWidget'; + +export default { + CheckboxWidget, + CheckboxesWidget, + DateWidget, + DateTimeWidget, + RadioWidget, + RangeWidget, + SelectWidget, + TextareaWidget, +}; diff --git a/spiffworkflow-frontend/src/themes/carbon/Widgets/index.ts b/spiffworkflow-frontend/src/themes/carbon/Widgets/index.ts new file mode 100644 index 00000000..de857bf5 --- /dev/null +++ b/spiffworkflow-frontend/src/themes/carbon/Widgets/index.ts @@ -0,0 +1,2 @@ +export { default } from './Widgets'; +export * from './Widgets'; diff --git a/spiffworkflow-frontend/src/themes/carbon/WrapIfAdditionalTemplate/WrapIfAdditionalTemplate.tsx b/spiffworkflow-frontend/src/themes/carbon/WrapIfAdditionalTemplate/WrapIfAdditionalTemplate.tsx new file mode 100644 index 00000000..954d82c4 --- /dev/null +++ b/spiffworkflow-frontend/src/themes/carbon/WrapIfAdditionalTemplate/WrapIfAdditionalTemplate.tsx @@ -0,0 +1,80 @@ +import React, { CSSProperties } from 'react'; +import FormControl from '@mui/material/FormControl'; +import Grid from '@mui/material/Grid'; +import InputLabel from '@mui/material/InputLabel'; +import Input from '@mui/material/OutlinedInput'; +import { + ADDITIONAL_PROPERTY_FLAG, + WrapIfAdditionalTemplateProps, +} from '@rjsf/utils'; + +function WrapIfAdditionalTemplate({ + children, + classNames, + disabled, + id, + label, + onDropPropertyClick, + onKeyChange, + readonly, + required, + schema, + uiSchema, + registry, +}: WrapIfAdditionalTemplateProps) { + // Button templates are not overridden in the uiSchema + const { RemoveButton } = registry.templates.ButtonTemplates; + const keyLabel = `${label} Key`; // i18n ? + const additional = ADDITIONAL_PROPERTY_FLAG in schema; + const btnStyle: CSSProperties = { + flex: 1, + paddingLeft: 6, + paddingRight: 6, + fontWeight: 'bold', + }; + + if (!additional) { + return
{children}
; + } + + const handleBlur = ({ target }: React.FocusEvent) => + onKeyChange(target.value); + + return ( + + + + {keyLabel} + + + + + {children} + + + + + + ); +} + +export default WrapIfAdditionalTemplate; diff --git a/spiffworkflow-frontend/src/themes/carbon/WrapIfAdditionalTemplate/index.ts b/spiffworkflow-frontend/src/themes/carbon/WrapIfAdditionalTemplate/index.ts new file mode 100644 index 00000000..7d7af662 --- /dev/null +++ b/spiffworkflow-frontend/src/themes/carbon/WrapIfAdditionalTemplate/index.ts @@ -0,0 +1,2 @@ +export { default } from './WrapIfAdditionalTemplate'; +export * from './WrapIfAdditionalTemplate'; diff --git a/spiffworkflow-frontend/src/themes/carbon/index.ts b/spiffworkflow-frontend/src/themes/carbon/index.ts new file mode 100644 index 00000000..7da74d8f --- /dev/null +++ b/spiffworkflow-frontend/src/themes/carbon/index.ts @@ -0,0 +1,8 @@ +import MuiForm from './MuiForm/MuiForm'; + +export { default as Form } from './MuiForm'; +export { default as Templates } from './Templates'; +export { default as Theme } from './Theme'; +export { default as Widgets } from './Widgets'; + +export default MuiForm;