UNPKG

@eclipse-scout/core

Version:
367 lines (316 loc) 14 kB
/* * Copyright (c) 2010, 2023 BSI Business Systems Integration AG * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 * which is available at https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 */ import { arrays, Button, CheckBoxField, CompositeField, EventHandler, FormField, GridData, GroupBox, HtmlComponent, HtmlEnvironment, InitModelOf, LabelField, PlaceholderField, PropertyChangeEvent, RadioButtonGroup, ResponsiveHandler, ResponsiveManager, SequenceBox, TreeVisitResult } from '../index'; export class GroupBoxResponsiveHandler extends ResponsiveHandler { declare widget: GroupBox; protected _compositeFields: CompositeField[]; protected _formFieldAddedHandler: EventHandler<PropertyChangeEvent<any, CompositeField>>; protected _htmlPropertyChangeHandler: EventHandler<PropertyChangeEvent<any, HtmlEnvironment>>; constructor() { super(); this._initDefaults(); this.allowedStates = [ResponsiveManager.ResponsiveState.NORMAL, ResponsiveManager.ResponsiveState.CONDENSED, ResponsiveManager.ResponsiveState.COMPACT]; this._formFieldAddedHandler = this._onFormFieldAdded.bind(this); this._compositeFields = []; this._htmlPropertyChangeHandler = this._onHtmlEnvironmentPropertyChange.bind(this); } static TransformationType = { LABEL_POSITION_ON_FIELD: 'labelPositionOnField', LABEL_POSITION_ON_TOP: 'labelPositionOnTop', LABEL_VISIBILITY: 'labelVisibility', STATUS_POSITION_ON_TOP: 'statusPositionOnTop', STATUS_VISIBILITY: 'statusVisibility', VERTICAL_ALIGNMENT: 'verticalAlignment', GRID_COLUMN_COUNT: 'gridColumnCount', HIDE_PLACE_HOLDER_FIELD: 'hidePlaceHolderField', FIELD_SCALABLE: 'fieldScalable', RADIO_BUTTON_GROUP_USE_UI_HEIGHT: 'radioButtonGroupUseUiHeight' } as const; protected _initDefaults() { this.compactThreshold = HtmlEnvironment.get().formColumnWidth; } protected _onHtmlEnvironmentPropertyChange() { this._initDefaults(); } override init(model: InitModelOf<this>) { super.init(model); let transformationType = GroupBoxResponsiveHandler.TransformationType; let responsiveState = ResponsiveManager.ResponsiveState; this._registerTransformation(transformationType.LABEL_POSITION_ON_FIELD, this._transformLabelPositionOnField); this._registerTransformation(transformationType.LABEL_POSITION_ON_TOP, this._transformLabelPositionOnTop); this._registerTransformation(transformationType.LABEL_VISIBILITY, this._transformLabelVisibility); this._registerTransformation(transformationType.STATUS_POSITION_ON_TOP, this._transformStatusPosition); this._registerTransformation(transformationType.STATUS_VISIBILITY, this._transformStatusVisibility); this._registerTransformation(transformationType.VERTICAL_ALIGNMENT, this._transformVerticalAlignment); this._registerTransformation(transformationType.GRID_COLUMN_COUNT, this._transformGridColumnCount); this._registerTransformation(transformationType.HIDE_PLACE_HOLDER_FIELD, this._transformHidePlaceHolderField); this._registerTransformation(transformationType.FIELD_SCALABLE, this._transformFieldScalable); this._registerTransformation(transformationType.RADIO_BUTTON_GROUP_USE_UI_HEIGHT, this._transformRadioButtonGroupUseUiHeight); this._enableTransformation(responsiveState.CONDENSED, transformationType.LABEL_POSITION_ON_TOP); this._enableTransformation(responsiveState.CONDENSED, transformationType.LABEL_VISIBILITY); this._enableTransformation(responsiveState.CONDENSED, transformationType.VERTICAL_ALIGNMENT); this._enableTransformation(responsiveState.CONDENSED, transformationType.RADIO_BUTTON_GROUP_USE_UI_HEIGHT); this._enableTransformation(responsiveState.COMPACT, transformationType.LABEL_POSITION_ON_TOP); this._enableTransformation(responsiveState.COMPACT, transformationType.LABEL_VISIBILITY); this._enableTransformation(responsiveState.COMPACT, transformationType.STATUS_POSITION_ON_TOP); this._enableTransformation(responsiveState.COMPACT, transformationType.STATUS_VISIBILITY); this._enableTransformation(responsiveState.COMPACT, transformationType.VERTICAL_ALIGNMENT); this._enableTransformation(responsiveState.COMPACT, transformationType.GRID_COLUMN_COUNT); this._enableTransformation(responsiveState.COMPACT, transformationType.HIDE_PLACE_HOLDER_FIELD); this._enableTransformation(responsiveState.COMPACT, transformationType.FIELD_SCALABLE); this._enableTransformation(responsiveState.COMPACT, transformationType.RADIO_BUTTON_GROUP_USE_UI_HEIGHT); HtmlEnvironment.get().on('propertyChange', this._htmlPropertyChangeHandler); this.widget.visitFields(field => { if (field instanceof CompositeField) { field.on('propertyChange', this._formFieldAddedHandler); this._compositeFields.push(field); } }, {limitToSameFieldTree: true}); } override destroy() { super.destroy(); this._compositeFields.forEach(compositeField => compositeField.off('propertyChange', this._formFieldAddedHandler)); HtmlEnvironment.get().off('propertyChange', this._htmlPropertyChangeHandler); } override active(): boolean { return this.widget.responsive; } override getCondensedThreshold(): number { if (this.condensedThreshold > 0) { return this.condensedThreshold; } return this.widget.htmlComp.prefSize({widthOnly: true}).width; } protected override _transform() { this.widget.visitFields(this._transformWidget.bind(this), {limitToSameFieldTree: true}); } protected override _transformWidget(widget: FormField): TreeVisitResult { // skip group boxes with responsiveness set. if (widget !== this.widget && widget instanceof GroupBox && widget.responsive !== null) { return TreeVisitResult.SKIP_SUBTREE; } // skip everything that is not a form field. if (!(widget instanceof FormField)) { return; } // suppress a validation of the layout tree, because these widgets will be validated later anyway. // the component still might be marked as invalid (which is necessary) caused by the responsive modifications applied. let htmlParent: HtmlComponent; if (widget.htmlComp) { widget.htmlComp.suppressValidate = true; htmlParent = widget.htmlComp.getParent(); if (htmlParent) { htmlParent.suppressValidate = true; } } super._transformWidget(widget); if (widget.htmlComp) { widget.htmlComp.suppressValidate = false; } if (htmlParent) { htmlParent.suppressValidate = false; } } /* --- TRANSFORMATIONS ------------------------------------------------------------- */ /** * Label Position -> ON_FIELD */ protected _transformLabelPositionOnField(field: FormField, apply: boolean) { if (field.parent instanceof SequenceBox || field instanceof CheckBoxField || field instanceof LabelField) { return; } if (apply) { this._storeFieldProperty(field, 'labelPosition', field.labelPosition); field.setLabelPosition(FormField.LabelPosition.ON_FIELD); } else { if (this._hasFieldProperty(field, 'labelPosition')) { field.setLabelPosition(this._getFieldProperty(field, 'labelPosition')); } } } /** * Label Position -> ON_TOP */ protected _transformLabelPositionOnTop(field: FormField, apply: boolean) { if (field.parent instanceof SequenceBox || field instanceof CheckBoxField || field instanceof LabelField || field.labelPosition === FormField.LabelPosition.ON_FIELD) { return; } if (apply) { this._storeFieldProperty(field, 'labelPosition', field.labelPosition); field.setLabelPosition(FormField.LabelPosition.TOP); } else { if (this._hasFieldProperty(field, 'labelPosition')) { field.setLabelPosition(this._getFieldProperty(field, 'labelPosition')); } } } /** * Label visibility */ protected _transformLabelVisibility(field: FormField, apply: boolean) { if (!(field instanceof CheckBoxField)) { return; } if (apply) { this._storeFieldProperty(field, 'labelVisible', field.labelVisible); field.setLabelVisible(false); } else { if (this._hasFieldProperty(field, 'labelVisible')) { field.setLabelVisible(this._getFieldProperty(field, 'labelVisible')); } } } // ScoutJs specific method. This methods will be overridden by GroupBoxAdapter for scout classic case. getGridData(field: FormField): GridData { return new GridData(field.gridDataHints); } // ScoutJs specific method. This methods will be overridden by GroupBoxAdapter for scout classic case. setGridData(field: FormField, gridData: GridData) { field.setGridDataHints(gridData); } /** * Status position */ protected _transformStatusPosition(field: FormField, apply: boolean) { if (apply) { this._storeFieldProperty(field, 'statusPosition', field.statusPosition); field.setStatusPosition(FormField.StatusPosition.TOP); } else { if (this._hasFieldProperty(field, 'statusPosition')) { field.setStatusPosition(this._getFieldProperty(field, 'statusPosition')); } } } /** * Status visibility */ protected _transformStatusVisibility(field: FormField, apply: boolean) { if (apply) { this._storeFieldProperty(field, 'statusVisible', field.statusVisible); field.setStatusVisible(false); } else { if (this._hasFieldProperty(field, 'statusVisible')) { field.setStatusVisible(this._getFieldProperty(field, 'statusVisible')); } } } /** * Set vertical align to bottom for checkboxes, buttons and fields with gridH = 1 and an on-field label * to make them align with fields having a top label in the same grid row. */ protected _transformVerticalAlignment(field: FormField, apply: boolean) { let isDefaultButton = field instanceof Button && field.displayStyle === Button.DisplayStyle.DEFAULT; let isCheckbox = field instanceof CheckBoxField; let gridData = this.getGridData(field); let isSingleHeightOnFieldLabelField = field.labelPosition === FormField.LabelPosition.ON_FIELD && gridData.h === 1; if (!(isDefaultButton || isCheckbox || isSingleHeightOnFieldLabelField) || !field.gridData) { return; } if (apply) { this._storeFieldProperty(field, 'fillVertical', gridData.fillVertical); this._storeFieldProperty(field, 'verticalAlignment', gridData.verticalAlignment); gridData.fillVertical = false; gridData.verticalAlignment = 1; } else { if (this._hasFieldProperty(field, 'fillVertical')) { gridData.fillVertical = this._getFieldProperty(field, 'fillVertical'); } if (this._hasFieldProperty(field, 'verticalAlignment')) { gridData.verticalAlignment = this._getFieldProperty(field, 'verticalAlignment'); } } this.setGridData(field, gridData); } /** * Column count */ protected _transformGridColumnCount(field: FormField, apply: boolean) { if (!(field instanceof GroupBox)) { return; } if (apply) { this._storeFieldProperty(field, 'gridColumnCount', field.gridColumnCount); field.setGridColumnCount(1); } else { if (this._hasFieldProperty(field, 'gridColumnCount')) { field.setGridColumnCount(this._getFieldProperty(field, 'gridColumnCount')); } } } /** * Hide placeholder field */ protected _transformHidePlaceHolderField(field: FormField, apply: boolean) { if (!(field instanceof PlaceholderField)) { return; } if (apply) { this._storeFieldProperty(field, 'visible', field.visible); field.setVisible(false); } else { if (this._hasFieldProperty(field, 'visible')) { field.setVisible(this._getFieldProperty(field, 'visible')); } } } /** * GroupBox: Makes sure weightX is set to 1 which makes the field scalable. * * Reason:<br> * The width of the field should be adjusted according to the display width, otherwise it may be too big to be * displayed. <br> * Additionally, since we use a one column layout, setting weightX to 0 might destroy the layout because it affects * all the fields in the groupBox. */ protected _transformFieldScalable(field: FormField, apply: boolean) { if (field.parent instanceof SequenceBox) { return; } let gridData = this.getGridData(field); if (apply && gridData.weightX === 0) { this._storeFieldProperty(field, 'weightX', gridData.weightX); gridData.weightX = 1; } else if (!apply) { if (this._hasFieldProperty(field, 'weightX')) { gridData.weightX = this._getFieldProperty(field, 'weightX'); } } this.setGridData(field, gridData); } protected _transformRadioButtonGroupUseUiHeight(field: FormField, apply: boolean) { if (!(field instanceof RadioButtonGroup)) { return; } let gridData = this.getGridData(field); if (apply && !gridData.useUiHeight) { this._storeFieldProperty(field, 'useUiHeight', gridData.useUiHeight); gridData.useUiHeight = true; } else if (!apply) { if (this._hasFieldProperty(field, 'useUiHeight')) { gridData.useUiHeight = this._getFieldProperty(field, 'useUiHeight'); } } this.setGridData(field, gridData); } /* --- HANDLERS ------------------------------------------------------------- */ protected _onFormFieldAdded(event: PropertyChangeEvent<any, CompositeField>) { if (this.state !== ResponsiveManager.ResponsiveState.NORMAL && (event.propertyName === 'fields' || event.propertyName === 'tabItems')) { let newFields = arrays.diff(event.newValue, event.oldValue) as CompositeField[]; newFields.forEach(field => field.visitFields(this._transformWidget.bind(this), {limitToSameFieldTree: true})); } } }