From 35314a345cb4b427c1bd62e547364d31fb2f3496 Mon Sep 17 00:00:00 2001 From: Vinicius Goulart Date: Tue, 4 Jun 2024 20:26:48 +0200 Subject: [PATCH] wip --- .eslintrc | 2 +- package-lock.json | 36 ++++- packages/form-js-viewer-core/index.html | 12 +- packages/form-js-viewer-core/mock-schema.json | 26 ++++ packages/form-js-viewer-core/package.json | 10 +- .../src/form-js-viewer-core.js | 123 ++++++++++++++++++ .../src/form-js-viewer-preact.jsx | 69 ++++++++++ .../src/form-js-viewer.jsx | 22 ++++ packages/form-js-viewer-core/src/index.js | 7 +- packages/form-js-viewer-core/vite.config.js | 10 ++ 10 files changed, 306 insertions(+), 11 deletions(-) create mode 100644 packages/form-js-viewer-core/mock-schema.json create mode 100644 packages/form-js-viewer-core/src/form-js-viewer-core.js create mode 100644 packages/form-js-viewer-core/src/form-js-viewer-preact.jsx create mode 100644 packages/form-js-viewer-core/src/form-js-viewer.jsx diff --git a/.eslintrc b/.eslintrc index 1311c72ea..68ec84bb9 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,6 +1,6 @@ { "parserOptions": { - "ecmaVersion": 2019 + "ecmaVersion": 2022 }, "extends": [ "plugin:bpmn-io/browser", diff --git a/package-lock.json b/package-lock.json index c83da7056..5696a04ef 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21983,7 +21983,25 @@ "packages/form-js-viewer-core": { "name": "@bpmn-io/form-js-viewer-core", "version": "1.8.6", - "license": "SEE LICENSE IN LICENSE" + "license": "SEE LICENSE IN LICENSE", + "dependencies": { + "feelin": "^3.1.0" + }, + "devDependencies": { + "@preact/preset-vite": "^2.8.2", + "min-dash": "^4.2.1", + "preact": "^10.22.0" + } + }, + "packages/form-js-viewer-core/node_modules/preact": { + "version": "10.22.0", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.22.0.tgz", + "integrity": "sha512-RRurnSjJPj4rp5K6XoP45Ui33ncb7e4H7WiOHVpjbkvqvA3U+N8Z6Qbo0AE6leGYBV66n8EhEaFixvIu3SkxFw==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } }, "packages/form-js-viewer-render": { "name": "@bpmn-io/form-js-viewer-render", @@ -23537,7 +23555,21 @@ } }, "@bpmn-io/form-js-viewer-core": { - "version": "file:packages/form-js-viewer-core" + "version": "file:packages/form-js-viewer-core", + "requires": { + "@preact/preset-vite": "^2.8.2", + "feelin": "^3.1.0", + "min-dash": "^4.2.1", + "preact": "^10.22.0" + }, + "dependencies": { + "preact": { + "version": "10.22.0", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.22.0.tgz", + "integrity": "sha512-RRurnSjJPj4rp5K6XoP45Ui33ncb7e4H7WiOHVpjbkvqvA3U+N8Z6Qbo0AE6leGYBV66n8EhEaFixvIu3SkxFw==", + "dev": true + } + } }, "@bpmn-io/form-js-viewer-render": { "version": "file:packages/form-js-viewer-render" diff --git a/packages/form-js-viewer-core/index.html b/packages/form-js-viewer-core/index.html index a166ff5c5..b14d626d3 100644 --- a/packages/form-js-viewer-core/index.html +++ b/packages/form-js-viewer-core/index.html @@ -9,10 +9,18 @@ +
diff --git a/packages/form-js-viewer-core/mock-schema.json b/packages/form-js-viewer-core/mock-schema.json new file mode 100644 index 000000000..aa8bea228 --- /dev/null +++ b/packages/form-js-viewer-core/mock-schema.json @@ -0,0 +1,26 @@ +{ + "components": [ + { + "label": "Text field", + "type": "textfield", + "id": "field_1knwu16", + "key": "aTextField" + }, + { + "label": "first_checkbox", + "type": "checkbox", + "id": "field_0zbcir4", + "key": "first_checkbox", + "conditional": { + "hide": "=second_checkbox" + } + }, + { + "label": "second_checkbox", + "type": "checkbox", + "id": "field_06pzj62", + "key": "second_checkbox" + } + ], + "type": "default" +} diff --git a/packages/form-js-viewer-core/package.json b/packages/form-js-viewer-core/package.json index 5529a89ab..f4b8b4103 100644 --- a/packages/form-js-viewer-core/package.json +++ b/packages/form-js-viewer-core/package.json @@ -31,5 +31,13 @@ "sideEffects": false, "files": [ "dist" - ] + ], + "dependencies": { + "feelin": "^3.1.0" + }, + "devDependencies": { + "@preact/preset-vite": "^2.8.2", + "min-dash": "^4.2.1", + "preact": "^10.22.0" + } } diff --git a/packages/form-js-viewer-core/src/form-js-viewer-core.js b/packages/form-js-viewer-core/src/form-js-viewer-core.js new file mode 100644 index 000000000..9b2332388 --- /dev/null +++ b/packages/form-js-viewer-core/src/form-js-viewer-core.js @@ -0,0 +1,123 @@ +import { evaluate } from 'feelin'; +import { get } from 'min-dash'; + +const EXPRESSION_PROPERTIES = ['key', 'conditional.hide']; + +class Core { + initialContext = null; + context = null; + schema = null; + subscribers = new Map(); + + constructor(options = {}) { + const { context = {}, schema } = options; + + if (!schema || !Array.isArray(schema?.components)) { + throw new Error('Invalid schema'); + } + + const parsedSchema = this.parseSchema(schema, context); + + parsedSchema.reduce((acc, component) => { + const { key, value } = component; + + return { ...acc, [key]: value }; + }, {}); + + this.initialContext = context; + this.context = { + ...context, + ...parsedSchema, + }; + this.schema = schema; + } + + parseSchema(schema, context) { + const components = []; + + schema.components.forEach((component) => { + const { type, key, ...rest } = component; + const hideProperty = get(component, ['conditional', 'hide']); + + if (hideProperty !== undefined && evaluate(hideProperty.replace(/^=/, ''), context)) { + return; + } + + if (type === 'textfield') { + components.push({ + type, + key, + value: evaluate(key, context), + ...rest, + }); + } + + if (type === 'checkbox') { + components.push({ + type, + key, + value: evaluate(key, context), + ...rest, + }); + } + }); + + return components; + } + + subscribe(fieldPath, callback) { + const field = get(this.context, fieldPath); + + if (!field) { + throw new Error(`Field ${fieldPath} not found`); + } + + if (!this.subscribers.has(fieldPath)) { + this.subscribers.set(fieldPath, []); + } + + this.subscribers.get(fieldPath).push(callback); + } + + unsubscribe(fieldPath, callback) { + const field = get(this.context, fieldPath); + + if (!field) { + throw new Error(`Field ${fieldPath} not found`); + } + + if (!this.subscribers.has(fieldPath)) { + return; + } + + const subscribers = this.subscribers.get(fieldPath); + + subscribers.splice(subscribers.indexOf(callback), 1); + } + + change(fieldPath, value) { + const field = get(this.context, fieldPath); + + if (!field) { + throw new Error(`Field ${fieldPath} not found`); + } + + if (field.type === 'textfield') { + field.value = value; + } + + if (field.type === 'checkbox') { + field.value = value; + } + + this.subscribers.forEach((subscribers, fieldPath) => { + if (fieldPath === field.key) { + subscribers.forEach((callback) => { + callback(field); + }); + } + }); + } +} + +export { Core }; diff --git a/packages/form-js-viewer-core/src/form-js-viewer-preact.jsx b/packages/form-js-viewer-core/src/form-js-viewer-preact.jsx new file mode 100644 index 000000000..2fa3335f2 --- /dev/null +++ b/packages/form-js-viewer-core/src/form-js-viewer-preact.jsx @@ -0,0 +1,69 @@ +import { createContext } from 'preact'; +import { useContext, useEffect, useState } from 'preact/hooks'; +import { Core } from './form-js-viewer-core'; +import { get } from 'min-dash'; + +const FormProvider = createContext(null); + +function useField(fieldPath) { + const form = useContext(FormProvider); + const [field, setField] = useState(get(form.context, fieldPath)); + + if (!form || !field) { + throw new Error('Form not initialized'); + } + + useEffect(() => { + const subscriber = (field) => { + setField(field); + }; + + form.subscribe(fieldPath, subscriber); + + return () => { + form.unsubscribe(fieldPath, subscriber); + }; + }, [fieldPath, form]); + + return field; +} + +function Form(props = {}) { + const { context, schema } = props; + + return ( + + + + ); +} + +function Components(props = {}) { + const form = useContext(FormProvider); + + console.log({ form }); + + return ( + <> + {form.schema.components.map((component) => ( + + ))} + + ); +} + +function Field(props = {}) { + const field = useField(props.fieldPath); + + if (field.type === 'textfield') { + return ; + } + + if (field.type === 'checkbox') { + return ; + } + + return <>{field}; +} + +export { Form, useField }; diff --git a/packages/form-js-viewer-core/src/form-js-viewer.jsx b/packages/form-js-viewer-core/src/form-js-viewer.jsx new file mode 100644 index 000000000..8f2097ad1 --- /dev/null +++ b/packages/form-js-viewer-core/src/form-js-viewer.jsx @@ -0,0 +1,22 @@ +import { render } from 'preact'; +import { Form } from './form-js-viewer-preact'; + +function renderForm(options = {}) { + const { container, context, schema } = options; + + if (container === null) { + return; + } + + console.log({ context, schema }); + + render(
, container); +} + +function Textfield(props = {}) { + const { value } = props; + + return ; +} + +export { renderForm }; diff --git a/packages/form-js-viewer-core/src/index.js b/packages/form-js-viewer-core/src/index.js index b3f624ff3..8fd8e3ff5 100644 --- a/packages/form-js-viewer-core/src/index.js +++ b/packages/form-js-viewer-core/src/index.js @@ -1,5 +1,2 @@ -function foo() { - return 'foo'; -} - -export { foo }; +export { Core } from './form-js-viewer-core'; +export { renderForm } from './form-js-viewer'; diff --git a/packages/form-js-viewer-core/vite.config.js b/packages/form-js-viewer-core/vite.config.js index cabf5cfcc..5c608d981 100644 --- a/packages/form-js-viewer-core/vite.config.js +++ b/packages/form-js-viewer-core/vite.config.js @@ -1,7 +1,17 @@ import { resolve } from 'path'; +import { createRequire } from 'module'; import { defineConfig } from 'vite'; +import preact from '@preact/preset-vite'; export default defineConfig({ + plugins: [ + preact({ + babel: { + // Change cwd to load Preact Babel plugins + cwd: createRequire(import.meta.url).resolve('@preact/preset-vite'), + }, + }), + ], build: { lib: { entry: resolve(__dirname, 'src/index.js'),