diff --git a/packages/form-js-editor/src/features/properties-panel/PropertiesPanel.js b/packages/form-js-editor/src/features/properties-panel/PropertiesPanel.js index 50314ac4f..80cf47467 100644 --- a/packages/form-js-editor/src/features/properties-panel/PropertiesPanel.js +++ b/packages/form-js-editor/src/features/properties-panel/PropertiesPanel.js @@ -3,80 +3,37 @@ import { PropertiesPanel as BasePropertiesPanel } from '@bpmn-io/properties-pane import { useCallback, useMemo, - useState, - useLayoutEffect } from 'preact/hooks'; import { reduce, isArray } from 'min-dash'; - -import { FormPropertiesPanelContext } from './context'; - import { PropertiesPanelHeaderProvider } from './PropertiesPanelHeaderProvider'; import { PropertiesPanelPlaceholderProvider } from './PropertiesPanelPlaceholderProvider'; +import { useService } from './hooks'; -export function PropertiesPanel(props) { - const { - eventBus, - getProviders, - injector - } = props; +export function PropertiesPanel() { - const formEditor = injector.get('formEditor'); - const modeling = injector.get('modeling'); - const selectionModule = injector.get('selection'); - const propertiesPanelConfig = injector.get('config.propertiesPanel') || {}; + const eventBus = useService('eventBus'); + const modeling = useService('modeling'); + const selection = useService('selection'); + const formEditor = useService('formEditor'); + const propertiesPanelConfig = useService('config.propertiesPanel') || {}; + const propertiesProviderRegistry = useService('propertiesProviderRegistry'); const { feelPopupContainer } = propertiesPanelConfig; - const [ state , setState ] = useState({ selectedFormField: selectionModule.get() || formEditor._getState().schema }); - - const selectedFormField = state.selectedFormField; - - const refresh = useCallback((field) => { - - // TODO(skaiir): rework state management, re-rendering the whole properties panel is not the way to go - // https://github.com/bpmn-io/form-js/issues/686 - setState({ selectedFormField: selectionModule.get() || formEditor._getState().schema }); - - // notify interested parties on property panel updates - eventBus.fire('propertiesPanel.updated', { - formField: field - }); - - }, [ eventBus, formEditor, selectionModule ]); - - - useLayoutEffect(() => { - - /** - * TODO(pinussilvestrus): update with actual updated element, - * once we have a proper updater/change support - */ - eventBus.on('changed', refresh); - eventBus.on('import.done', refresh); - eventBus.on('selection.changed', refresh); - - return () => { - eventBus.off('changed', refresh); - eventBus.off('import.done', refresh); - eventBus.off('selection.changed', refresh); - }; - }, [ eventBus, refresh ]); - - const getService = (type, strict = true) => injector.get(type, strict); - - const propertiesPanelContext = { getService }; - const onFocus = () => eventBus.fire('propertiesPanel.focusin'); const onBlur = () => eventBus.fire('propertiesPanel.focusout'); const editField = useCallback((formField, key, value) => modeling.editFormField(formField, key, value), [ modeling ]); - // retrieve groups for selected form field - const providers = getProviders(selectedFormField); + const selectedFormField = selection.get() || formEditor._getState().schema; + + const providers = useMemo(() => { + return propertiesProviderRegistry.getProviders(selectedFormField); + }, [ propertiesProviderRegistry, selectedFormField ]); const groups = useMemo(() => { return reduce(providers, function(groups, provider) { @@ -93,13 +50,13 @@ export function PropertiesPanel(props) { }, [ providers, selectedFormField, editField ]); return ( -
- +
+
- +
); } \ No newline at end of file diff --git a/packages/form-js-editor/src/features/properties-panel/PropertiesPanelModule.js b/packages/form-js-editor/src/features/properties-panel/PropertiesPanelModule.js deleted file mode 100644 index 7deef8984..000000000 --- a/packages/form-js-editor/src/features/properties-panel/PropertiesPanelModule.js +++ /dev/null @@ -1,7 +0,0 @@ -import { SectionModuleBase } from '../SectionModuleBase'; - -export class PropertiesPanelModule extends SectionModuleBase { - constructor(eventBus) { super(eventBus, 'propertiesPanel'); } -} - -PropertiesPanelModule.$inject = [ 'eventBus' ]; \ No newline at end of file diff --git a/packages/form-js-editor/src/features/properties-panel/PropertiesPanelRenderer.js b/packages/form-js-editor/src/features/properties-panel/PropertiesPanelRenderer.js index eae163820..e14f3412d 100644 --- a/packages/form-js-editor/src/features/properties-panel/PropertiesPanelRenderer.js +++ b/packages/form-js-editor/src/features/properties-panel/PropertiesPanelRenderer.js @@ -1,145 +1,7 @@ -import { PropertiesPanel } from './PropertiesPanel'; +import { SectionModuleBase } from '../SectionModuleBase'; -import { - render -} from 'preact'; - -import { - domify, - query as domQuery -} from 'min-dom'; - -const DEFAULT_PRIORITY = 1000; - -/** - * @typedef { { parent: Element } } PropertiesPanelConfig - * @typedef { import('../../core/EventBus').EventBus } EventBus - * @typedef { import('../../types').Injector } Injector - * @typedef { { getGroups: ({ formField, editFormField }) => ({ groups}) => Array } } PropertiesProvider - */ - -/** - * @param {PropertiesPanelConfig} propertiesPanelConfig - * @param {Injector} injector - * @param {EventBus} eventBus - */ -export class PropertiesPanelRenderer { - - constructor(propertiesPanelConfig, injector, eventBus) { - const { - parent - } = propertiesPanelConfig || {}; - - this._eventBus = eventBus; - this._injector = injector; - - this._container = domify('
'); - - if (parent) { - this.attachTo(parent); - } - - this._eventBus.once('formEditor.rendered', 500, () => { - this._render(); - }); - } - - - /** - * Attach the properties panel to a parent node. - * - * @param {HTMLElement} container - */ - attachTo(container) { - if (!container) { - throw new Error('container required'); - } - - if (typeof container === 'string') { - container = domQuery(container); - } - - // (1) detach from old parent - this.detach(); - - // (2) append to parent container - container.appendChild(this._container); - - // (3) notify interested parties - this._eventBus.fire('propertiesPanel.attach'); - } - - /** - * Detach the properties panel from its parent node. - */ - detach() { - const parentNode = this._container.parentNode; - - if (parentNode) { - parentNode.removeChild(this._container); - - this._eventBus.fire('propertiesPanel.detach'); - } - } - - _render() { - render( - , - this._container - ); - - this._eventBus.fire('propertiesPanel.rendered'); - } - - _destroy() { - if (this._container) { - render(null, this._container); - - this._eventBus.fire('propertiesPanel.destroyed'); - } - } - - /** - * Register a new properties provider to the properties panel. - * - * @param {PropertiesProvider} provider - * @param {Number} [priority] - */ - registerProvider(provider, priority) { - - if (!priority) { - priority = DEFAULT_PRIORITY; - } - - if (typeof provider.getGroups !== 'function') { - console.error( - 'Properties provider does not implement #getGroups(element) API' - ); - - return; - } - - this._eventBus.on('propertiesPanel.getProviders', priority, function(event) { - event.providers.push(provider); - }); - - this._eventBus.fire('propertiesPanel.providersChanged'); - } - - _getProviders() { - const event = this._eventBus.createEvent({ - type: 'propertiesPanel.getProviders', - providers: [] - }); - - this._eventBus.fire(event); - - return event.providers; - } +export class PropertiesPanelRenderer extends SectionModuleBase { + constructor(eventBus) { super(eventBus, 'propertiesPanel'); } } -PropertiesPanelRenderer.$inject = [ 'config.propertiesPanel', 'injector', 'eventBus' ]; \ No newline at end of file +PropertiesPanelRenderer.$inject = [ 'eventBus' ]; \ No newline at end of file diff --git a/packages/form-js-editor/src/features/properties-panel/PropertiesProvider.js b/packages/form-js-editor/src/features/properties-panel/PropertiesProvider.js index f73c18f55..13988b9ab 100644 --- a/packages/form-js-editor/src/features/properties-panel/PropertiesProvider.js +++ b/packages/form-js-editor/src/features/properties-panel/PropertiesProvider.js @@ -15,9 +15,9 @@ import { import { hasEntryConfigured } from './Util'; export class PropertiesProvider { - constructor(propertiesPanel, injector) { + constructor(propertiesProviderRegistry, injector) { this._injector = injector; - propertiesPanel.registerProvider(this); + propertiesProviderRegistry.registerProvider(this); } _filterVisibleEntries(groups, field, getService) { @@ -85,4 +85,4 @@ export class PropertiesProvider { } } -PropertiesProvider.$inject = [ 'propertiesPanel', 'injector' ]; \ No newline at end of file +PropertiesProvider.$inject = [ 'propertiesProviderRegistry', 'injector' ]; \ No newline at end of file diff --git a/packages/form-js-editor/src/features/properties-panel/PropertiesProviderRegistry.js b/packages/form-js-editor/src/features/properties-panel/PropertiesProviderRegistry.js new file mode 100644 index 000000000..4489a08b0 --- /dev/null +++ b/packages/form-js-editor/src/features/properties-panel/PropertiesProviderRegistry.js @@ -0,0 +1,56 @@ +const DEFAULT_PRIORITY = 1000; + +/** + * @typedef { import('../../core/EventBus').EventBus } EventBus + * @typedef { { getGroups: ({ formField, editFormField }) => ({ groups}) => Array } } PropertiesProvider + */ + +/** + * @param {EventBus} eventBus + */ +export class PropertiesProviderRegistry { + + constructor(eventBus) { + this._eventBus = eventBus; + } + + /** + * Register a new properties provider to the properties panel. + * + * @param {PropertiesProvider} provider + * @param {Number} [priority] + */ + registerProvider(provider, priority) { + + if (!priority) { + priority = DEFAULT_PRIORITY; + } + + if (typeof provider.getGroups !== 'function') { + console.error( + 'Properties provider does not implement #getGroups(element) API' + ); + + return; + } + + this._eventBus.on('propertiesPanel.getProviders', priority, function(event) { + event.providers.push(provider); + }); + + this._eventBus.fire('propertiesPanel.providersChanged'); + } + + getProviders() { + const event = this._eventBus.createEvent({ + type: 'propertiesPanel.getProviders', + providers: [] + }); + + this._eventBus.fire(event); + + return event.providers; + } +} + +PropertiesProviderRegistry.$inject = [ 'eventBus' ]; \ No newline at end of file diff --git a/packages/form-js-editor/src/features/properties-panel/context/FormPropertiesPanelContext.js b/packages/form-js-editor/src/features/properties-panel/context/FormPropertiesPanelContext.js deleted file mode 100644 index d82e5bf59..000000000 --- a/packages/form-js-editor/src/features/properties-panel/context/FormPropertiesPanelContext.js +++ /dev/null @@ -1,13 +0,0 @@ -import { createContext } from 'preact'; - -/** - * @param {string} type - * @param {boolean} [strict] - * - * @returns {any} - */ -function getService(type, strict) {} - -export const FormPropertiesPanelContext = createContext({ - getService -}); diff --git a/packages/form-js-editor/src/features/properties-panel/context/index.js b/packages/form-js-editor/src/features/properties-panel/context/index.js deleted file mode 100644 index 833d0fb3b..000000000 --- a/packages/form-js-editor/src/features/properties-panel/context/index.js +++ /dev/null @@ -1 +0,0 @@ -export { FormPropertiesPanelContext } from './FormPropertiesPanelContext'; \ No newline at end of file diff --git a/packages/form-js-editor/src/features/properties-panel/hooks/index.js b/packages/form-js-editor/src/features/properties-panel/hooks/index.js index 42d3b01d0..bb2448fbe 100644 --- a/packages/form-js-editor/src/features/properties-panel/hooks/index.js +++ b/packages/form-js-editor/src/features/properties-panel/hooks/index.js @@ -1,2 +1,2 @@ export { useVariables } from './useVariables'; -export { useService } from './usePropertiesPanelService'; \ No newline at end of file +export { useService } from './useService'; \ No newline at end of file diff --git a/packages/form-js-editor/src/features/properties-panel/hooks/usePropertiesPanelService.js b/packages/form-js-editor/src/features/properties-panel/hooks/usePropertiesPanelService.js deleted file mode 100644 index 7ea0335d5..000000000 --- a/packages/form-js-editor/src/features/properties-panel/hooks/usePropertiesPanelService.js +++ /dev/null @@ -1,14 +0,0 @@ -import { - useContext -} from 'preact/hooks'; - -import { FormPropertiesPanelContext } from '../context'; - - -export function useService(type, strict) { - const { - getService - } = useContext(FormPropertiesPanelContext); - - return getService(type, strict); -} \ No newline at end of file diff --git a/packages/form-js-editor/src/features/properties-panel/hooks/useService.js b/packages/form-js-editor/src/features/properties-panel/hooks/useService.js new file mode 100644 index 000000000..c415a72fa --- /dev/null +++ b/packages/form-js-editor/src/features/properties-panel/hooks/useService.js @@ -0,0 +1 @@ +export { useService } from '../../../render/hooks'; \ No newline at end of file diff --git a/packages/form-js-editor/src/features/properties-panel/hooks/useVariables.js b/packages/form-js-editor/src/features/properties-panel/hooks/useVariables.js index ed85bff07..cb86aa0bf 100644 --- a/packages/form-js-editor/src/features/properties-panel/hooks/useVariables.js +++ b/packages/form-js-editor/src/features/properties-panel/hooks/useVariables.js @@ -1,5 +1,5 @@ import { getSchemaVariables } from '@bpmn-io/form-js-viewer'; -import { useService } from './usePropertiesPanelService'; +import { useService } from './useService'; /** * Retrieve list of variables from the form schema. diff --git a/packages/form-js-editor/src/features/properties-panel/index.js b/packages/form-js-editor/src/features/properties-panel/index.js index 906666564..99c09cdb0 100644 --- a/packages/form-js-editor/src/features/properties-panel/index.js +++ b/packages/form-js-editor/src/features/properties-panel/index.js @@ -1,4 +1,5 @@ import { PropertiesPanelRenderer } from './PropertiesPanelRenderer'; +import { PropertiesProviderRegistry } from './PropertiesProviderRegistry'; import { PropertiesProvider } from './PropertiesProvider'; import { FeelPopupModule } from '@bpmn-io/properties-panel'; @@ -9,5 +10,6 @@ export const PropertiesPanelModule = { ], __init__: [ 'propertiesPanel', 'propertiesProvider' ], propertiesPanel: [ 'type', PropertiesPanelRenderer ], - propertiesProvider: [ 'type', PropertiesProvider ] + propertiesProvider: [ 'type', PropertiesProvider ], + propertiesProviderRegistry: [ 'type', PropertiesProviderRegistry ] }; \ No newline at end of file diff --git a/packages/form-js-editor/src/render/components/FormEditor.js b/packages/form-js-editor/src/render/components/FormEditor.js index c059fd588..0ea648bb6 100644 --- a/packages/form-js-editor/src/render/components/FormEditor.js +++ b/packages/form-js-editor/src/render/components/FormEditor.js @@ -31,6 +31,7 @@ import { DeleteIcon, DraggableIcon } from './icons'; import { ModularSection } from './ModularSection'; import { Palette, collectPaletteEntries, getPaletteIcon } from '../../features/palette/components/Palette'; import { InjectedRendersRoot } from '../../features/render-injection/components/InjectedRendersRoot'; +import { PropertiesPanel } from '../../features/properties-panel/PropertiesPanel'; import { SlotFillRoot } from '../../features/render-injection/slot-fill'; @@ -341,9 +342,7 @@ export function FormEditor() { eventBus = useService('eventBus'), formEditor = useService('formEditor'), injector = useService('injector'), - selection = useService('selection'), - propertiesPanel = useService('propertiesPanel'), - propertiesPanelConfig = useService('config.propertiesPanel'); + selection = useService('selection'); const { schema, properties } = formEditor._getState(); @@ -351,8 +350,6 @@ export function FormEditor() { const formContainerRef = useRef(null); - const propertiesPanelRef = useRef(null); - const [ , setSelection ] = useState(schema); const [ hasInitialized, setHasInitialized ] = useState(false); @@ -494,15 +491,6 @@ export function FormEditor() { const onReset = useCallback(() => {}, []); - // attach default properties panel - const hasDefaultPropertiesPanel = defaultPropertiesPanel(propertiesPanelConfig); - - useEffect(() => { - if (hasDefaultPropertiesPanel) { - propertiesPanel.attachTo(propertiesPanelRef.current); - } - }, [ propertiesPanelRef, propertiesPanel, hasDefaultPropertiesPanel ]); - return (
@@ -522,7 +510,9 @@ export function FormEditor() {
- { hasDefaultPropertiesPanel &&
} + + + @@ -623,10 +613,6 @@ function findPaletteEntry(type, formFields) { return collectPaletteEntries(formFields).find(entry => entry.type === type); } -function defaultPropertiesPanel(propertiesPanelConfig) { - return !(propertiesPanelConfig && propertiesPanelConfig.parent); -} - function getRemoveButtonTitle(formField, formFields) { const entry = findPaletteEntry(formField.type, formFields); diff --git a/packages/form-js-playground/test/custom/editor.js b/packages/form-js-playground/test/custom/editor.js index f659dd821..2eef5f420 100644 --- a/packages/form-js-playground/test/custom/editor.js +++ b/packages/form-js-playground/test/custom/editor.js @@ -7,8 +7,8 @@ import { class CustomPropertiesProvider { - constructor(propertiesPanel) { - propertiesPanel.registerProvider(this, 500); + constructor(propertiesProviderRegistry) { + propertiesProviderRegistry.registerProvider(this, 500); } getGroups(field, editField) { @@ -31,7 +31,7 @@ class CustomPropertiesProvider { } } -CustomPropertiesProvider.$inject = [ 'propertiesPanel' ]; +CustomPropertiesProvider.$inject = [ 'propertiesProviderRegistry' ]; function RangeEntries(field, editField) {