UNPKG

@eclipse-scout/core

Version:
260 lines (217 loc) 7.31 kB
/* * Copyright (c) 2010, 2025 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 { AbstractLayout, arrays, CodeLookupCall, CodeType, HtmlComponent, InitModelOf, LookupBoxEventMap, LookupBoxModel, LookupCall, LookupCallOrModel, LookupResult, LookupRow, objects, PropertyChangeEvent, scout, Status, strings, ValueField, Widget } from '../../index'; import $ from 'jquery'; export abstract class LookupBox<TValue> extends ValueField<TValue[], TValue | TValue[]> implements LookupBoxModel<TValue> { declare model: LookupBoxModel<TValue>; declare eventMap: LookupBoxEventMap<TValue>; declare self: LookupBox<any>; filterBox: Widget; lookupCall: LookupCall<TValue>; codeType: string | (new() => CodeType<TValue>); lookupStatus: Status; protected _currentLookupCall: LookupCall<TValue>; protected _lookupExecuted: boolean; /** true when value is either syncing to table or table to value */ protected _valueSyncing: boolean; constructor() { super(); this.filterBox = null; this.gridDataHints.weightY = 1.0; this.gridDataHints.h = 2; this.value = []; this.clearable = ValueField.Clearable.NEVER; this.lookupCall = null; this._currentLookupCall = null; this._lookupExecuted = false; this._valueSyncing = false; this._addCloneProperties(['lookupCall']); } static ErrorCode = { NO_DATA: 1 }; protected override _init(model: InitModelOf<this>) { super._init(model); if (this.filterBox) { this.filterBox.enabledComputed = true; // filter is always enabled this.filterBox.recomputeEnabled(true); this.filterBox.on('propertyChange', this._onFilterBoxPropertyChange.bind(this)); } } protected override _initValue(value: TValue[]) { if (this.lookupCall) { this._setLookupCall(this.lookupCall); } this._setCodeType(this.codeType); this._initStructure(value); super._initValue(value); } protected abstract _initStructure(value: TValue[]); protected override _render() { this.addContainer(this.$parent, 'lookup-box'); this.addLabel(); this.addMandatoryIndicator(); this.addStatus(); this.addFieldContainer(this.$parent.makeDiv()); let htmlComp = HtmlComponent.install(this.$fieldContainer, this.session); htmlComp.setLayout(this._createFieldContainerLayout()); this._ensureLookupCallExecuted(); this._renderStructure(); this.$field.addDeviceClass(); this.$field.addClass('structure'); this._renderFilterBox(); this.$container.css('--inactive-lookup-row-suffix-text', `'${this.session.text('InactiveState')}'`); } protected abstract _createFieldContainerLayout(): AbstractLayout; protected abstract _renderStructure(); protected _renderFilterBox() { if (!this.filterBox || !this.filterBox.visible) { return; } this.filterBox.render(this.$fieldContainer); } protected override _ensureValue(value: TValue | TValue[]): TValue[] { return arrays.ensure(value); } protected _lookupByAll(): JQuery.Promise<LookupResult<TValue>> { if (!this.lookupCall) { return; } let deferred = $.Deferred<LookupResult<TValue>>(); this._executeLookup(this.lookupCall.cloneForAll(), true) .done(result => { this._lookupByAllDone(result); deferred.resolve(result); }); return deferred.promise(); } protected _executeLookup(lookupCall: LookupCall<TValue>, abortExisting?: boolean): JQuery.Promise<LookupResult<TValue>> { this.setLoading(true); if (abortExisting && this._currentLookupCall) { this._currentLookupCall.abort(); } this._currentLookupCall = lookupCall; this.trigger('prepareLookupCall', { lookupCall: lookupCall }); return lookupCall .execute() .always(() => { this._currentLookupCall = null; this._lookupExecuted = true; this.setLoading(false); this._clearLookupStatus(); }); } protected _lookupByAllDone(result: LookupResult<TValue>) { try { if (result.exception) { // Oops! Something went wrong while the lookup has been processed. this.setLookupStatus(Status.warning({ // Use warning instead of error to allow form to be closed because user probably cannot fix lookup errors message: result.exception })); } } finally { this.trigger('lookupCallDone', { result: result }); } } protected override _errorStatus(): Status { return this.lookupStatus || this.errorStatus; } setLookupStatus(lookupStatus: Status) { this.setProperty('lookupStatus', lookupStatus); if (this.rendered) { this._renderErrorStatus(); } } override clearErrorStatus() { this.setErrorStatus(null); this._clearLookupStatus(); } protected _clearLookupStatus() { this.setLookupStatus(null); } /** @see LookupBoxModel.lookupCall */ setLookupCall(lookupCall: LookupCallOrModel<TValue>) { this.setProperty('lookupCall', lookupCall); } protected _setLookupCall(lookupCall: LookupCallOrModel<TValue>) { this._setProperty('lookupCall', LookupCall.ensure(lookupCall, this.session)); this._lookupExecuted = false; if (this.rendered) { this._ensureLookupCallExecuted(); } } /** @see LookupBoxModel.codeType */ setCodeType(codeType: string | (new() => CodeType<TValue>)) { this.setProperty('codeType', codeType); } protected _setCodeType(codeType: string | (new() => CodeType<TValue>)) { this._setProperty('codeType', codeType); if (!codeType) { return; } let lookupCall = scout.create(CodeLookupCall<TValue>, { session: this.session, codeType: codeType }); this.setLookupCall(lookupCall); } refreshLookup() { this._lookupExecuted = false; this._ensureLookupCallExecuted(); } /** * @returns true if a lookup call execution has been scheduled now. false otherwise. */ protected _ensureLookupCallExecuted(): boolean { if (this._lookupExecuted) { return false; } this._lookupByAll(); return true; } protected override _formatValue(value: TValue[]): string | JQuery.Promise<string> { if (objects.isNullOrUndefined(value)) { return ''; } return this._formatLookupRows(this.getCheckedLookupRows()); } abstract getCheckedLookupRows(): LookupRow<TValue>[]; protected _formatLookupRows(lookupRows: LookupRow<TValue>[]): string { lookupRows = arrays.ensure(lookupRows); if (lookupRows.length === 0) { return ''; } let formatted: string[] = []; lookupRows.forEach(row => formatted.push(row.text)); return strings.join(', ', ...formatted); } protected override _clear() { this.setValue(null); } protected _onFilterBoxPropertyChange(event: PropertyChangeEvent<any, Widget>) { if (event.propertyName === 'visible') { if (!this.rendered) { return; } if (this.filterBox.visible) { this._renderFilterBox(); } else { this.filterBox.remove(); } } } }