Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wip: reactive properties panel #1113

Draft
wants to merge 1 commit into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -93,13 +50,13 @@ export function PropertiesPanel(props) {
}, [ providers, selectedFormField, editField ]);

return (
<div
class="fjs-properties-panel"
data-field={ selectedFormField && selectedFormField.id }
onFocusCapture={ onFocus }
onBlurCapture={ onBlur }
>
<FormPropertiesPanelContext.Provider value={ propertiesPanelContext }>
<div class="fjs-properties-container" input-handle-modified-keys="y,z">
<div
class="fjs-properties-panel"
data-field={ selectedFormField && selectedFormField.id }
onFocusCapture={ onFocus }
onBlurCapture={ onBlur }
>
<BasePropertiesPanel
element={ selectedFormField }
eventBus={ eventBus }
Expand All @@ -108,7 +65,7 @@ export function PropertiesPanel(props) {
placeholderProvider={ PropertiesPanelPlaceholderProvider }
feelPopupContainer={ feelPopupContainer }
/>
</FormPropertiesPanelContext.Provider>
</div>
</div>
);
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -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('<div class="fjs-properties-container" input-handle-modified-keys="y,z"></div>');

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(
<PropertiesPanel
getProviders={ this._getProviders.bind(this) }
eventBus={ this._eventBus }
injector={ this._injector }
/>,
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' ];
PropertiesPanelRenderer.$inject = [ 'eventBus' ];
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -85,4 +85,4 @@ export class PropertiesProvider {
}
}

PropertiesProvider.$inject = [ 'propertiesPanel', 'injector' ];
PropertiesProvider.$inject = [ 'propertiesProviderRegistry', 'injector' ];
Original file line number Diff line number Diff line change
@@ -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' ];

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export { useVariables } from './useVariables';
export { useService } from './usePropertiesPanelService';
export { useService } from './useService';
Loading
Loading