@eclipse-scout/core
Version:
Eclipse Scout runtime
231 lines (195 loc) • 7.84 kB
text/typescript
/*
* 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 {aria, LookupRow, objects, ProposalFieldEventMap, ProposalFieldModel, SmartField, SmartFieldLookupResult, strings} from '../../../index';
import $ from 'jquery';
export class ProposalField extends SmartField<string> implements ProposalFieldModel {
declare model: ProposalFieldModel;
declare eventMap: ProposalFieldEventMap;
declare self: ProposalField;
trimText: boolean;
lookupOnAcceptByText: boolean;
constructor() {
super();
this.maxLength = 4000;
this.trimText = true;
this.lookupOnAcceptByText = false;
this._addCloneProperties(['lookupOnAcceptByText']);
}
protected override _getValueFromLookupRow(lookupRow: LookupRow<string>): string {
return lookupRow.text;
}
protected override _getLastSearchText(): string {
return this.value;
}
override cssClassName(): string {
return 'proposal-field';
}
protected override _addAriaFieldDescription() {
aria.addHiddenDescriptionAndLinkToElement(this.$field, this.id + '-func-desc', this.session.text('ui.AriaProposalFieldDescription'));
}
protected override _handleEnterKey(event: JQuery.KeyDownEvent) {
// The state of 'this.popup' is different on various browsers. On some browsers (IE11) we don't
// do CSS animations. This means IE11 sets the popup to null immediately whereas other browsers
// use a timeout. Anyway: in case the popup is open at the time the user presses enter, we must
// stop propagation (e.g. to avoid calls of other registered enter key-shortcuts, like the default
// button on a form). See Widget.js for details about removing with or without CSS animations.
let hasPopup = !!this.popup;
let promise = this.acceptInput();
if (promise) {
promise.always(this.closePopup.bind(this));
} else {
this.closePopup();
}
if (hasPopup) {
event.stopPropagation();
}
}
protected override _lookupByTextOrAllDone(result: SmartFieldLookupResult<string>) {
if (super._handleException(result)) {
return;
}
if (result.lookupRows.length === 0) {
this.setLoading(false);
this._handleEmptyResult();
return;
}
super._lookupByTextOrAllDone(result);
}
protected override _formatValue(value: string): string {
if (objects.isNullOrUndefined(value)) {
return '';
}
if (this.lookupRow) {
return this._formatLookupRow(this.lookupRow);
}
return value;
}
protected override _validateValue(value: string): string {
if (objects.isNullOrUndefined(value)) {
return value;
}
let validValue = strings.asString(value);
if (this.trimText) {
validValue = validValue.trim();
}
if (validValue === '') {
validValue = null;
}
return validValue;
}
protected override _ensureValue(value: string): string {
return strings.asString(value);
}
/**
* When 'clear' has been clicked (searchText is empty), we want to call customTextAccepted,
* so the new value is sent to the server #221199.
*/
protected override _acceptByText(sync: boolean, searchText: string) {
$.log.isDebugEnabled() && $.log.debug('(ProposalField#_acceptByText) searchText=', searchText);
let async = !sync;
// In case sync=true we cannot wait for the results of the lookup-call,
// that's why we simply accept the text that's already in the field
if (async && this.lookupOnAcceptByText && strings.hasText(searchText)) {
super._acceptByTextAsync(searchText);
} else {
this._customTextAccepted(searchText);
}
}
/**
* Only used in case lookupOnAcceptByText is true. It's basically the same code
* as in the smart-field but without the error handling.
*/
protected override _acceptByTextDone(result: SmartFieldLookupResult<string>) {
this._userWasTyping = false;
this._extendResult(result);
if (this.isPopupOpen()) {
this.popup.setLookupResult(result);
}
// when there's exactly one result, we accept that lookup row
if (result.uniqueMatch) {
let lookupRow = result.uniqueMatch;
if (this._isLookupRowActive(lookupRow)) {
this.setLookupRow(lookupRow);
this._inputAccepted();
return;
}
}
this._customTextAccepted(result.text);
}
protected override _checkResetLookupRow(value: string): boolean {
return this.lookupRow && this.lookupRow.text !== value;
}
protected override _checkSearchTextChanged(searchText: string): boolean {
return this._checkDisplayTextChanged(searchText);
}
protected _customTextAccepted(searchText: string) {
this._setLookupRow(null); // only reset property lookup
this._setValue(searchText);
this._inputAccepted(true, false);
}
override getValueForSelection(): string {
return this._showSelection() ? this.lookupRow.key : null;
}
/**
* In ProposalField value and display-text is the same. When a custom text has been entered,
* the value is set and the lookup-row is null.
*/
protected override _copyValuesFromField(otherField: ProposalField) {
this.lookupSeqNo = otherField.lookupSeqNo;
if (this.lookupRow !== otherField.lookupRow) {
this._setLookupRow(otherField.lookupRow); // only set property lookup
}
this.setErrorStatus(otherField.errorStatus);
this.setDisplayText(otherField.displayText);
this.setValue(otherField.value);
}
protected override _acceptInput(sync: boolean, searchText: string, searchTextEmpty: boolean, searchTextChanged: boolean, selectedLookupRow: LookupRow<string>): JQuery.Promise<void> | void {
if (this.touchMode) {
$.log.isDebugEnabled() && $.log.debug('(ProposalField#_acceptInput) Always send acceptInput for touch field');
// When the lookup is accepted by text (e.g. when lookupOnAcceptByText = true), the SmartField#acceptInput cannot
// detect the selectedLookupRow, because it just takes the selected row of the Proposal chooser
this._inputAccepted(true, !!this.lookupRow);
return;
}
// 1. Do nothing when search text did not change and is equals to the text of the current lookup row
if (!searchTextChanged && !selectedLookupRow && this.lookupRow && this.lookupRow.text === searchText) {
$.log.isDebugEnabled() && $.log.debug('(ProposalField#_acceptInput) unchanged: text is equals. Close popup');
this._inputAccepted(false);
return;
}
// 2. proposal chooser is open -> use the selected row as value
if (selectedLookupRow) {
$.log.isDebugEnabled() && $.log.debug('(ProposalField#_acceptInput) lookup-row selected. Set lookup-row, close popup lookupRow=', selectedLookupRow.toString());
this.clearErrorStatus();
this.setLookupRow(selectedLookupRow);
this._inputAccepted();
return;
}
// 3. proposal chooser is not open -> try to accept the current display text
// this causes a lookup which may fail and open a new proposal chooser (property
// change for 'result').
if (searchTextChanged) {
this.clearErrorStatus();
this._acceptByText(sync, searchText);
} else if (!this._hasUiError()) {
this._inputAccepted(false);
} else {
// even though there's nothing to do, someone could wait for our promise to be resolved
this._acceptInputDeferred.resolve();
}
return this._acceptInputDeferred.promise();
}
setTrimText(trimText: boolean) {
this.setProperty('trimText', trimText);
}
protected override _computeEmpty() {
return strings.empty(this.value);
}
}