@eclipse-scout/core
Version:
Eclipse Scout runtime
257 lines (217 loc) • 7.5 kB
text/typescript
/*
* 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, Device, DragAndDropOptions, EnumObject, Event, FileInput, FileInputChangeEvent, FormField, HtmlComponent, Icon, ImageFieldEventMap, ImageFieldLayout, ImageFieldModel, InitModelOf, scout, scrollbars, SingleLayout
} from '../../../index';
import $ from 'jquery';
export class ImageField extends FormField implements ImageFieldModel {
declare model: ImageFieldModel;
declare eventMap: ImageFieldEventMap;
declare self: ImageField;
autoFit: boolean;
imageUrl: string;
scrollBarEnabled: boolean;
uploadEnabled: boolean;
acceptTypes: string;
maximumUploadSize: number;
icon: Icon;
fileInput: FileInput;
protected _clickHandler: (event: JQuery.ClickEvent) => void;
constructor() {
super();
this.defaultMenuTypes = [...this.defaultMenuTypes, ImageField.MenuType.ImageUrl, ImageField.MenuType.Null];
this.autoFit = false;
this.imageUrl = null;
this.scrollBarEnabled = false;
this.uploadEnabled = false;
this.acceptTypes = null;
this.maximumUploadSize = FileInput.DEFAULT_MAXIMUM_UPLOAD_SIZE;
this._clickHandler = null;
}
static MenuType = {
Null: 'ImageField.Null',
ImageUrl: 'ImageField.ImageUrl'
} as const;
protected override _init(model: InitModelOf<this>) {
super._init(model);
this.resolveIconIds(['imageUrl']);
this.icon = scout.create(Icon, {
parent: this,
iconDesc: this.imageUrl,
autoFit: this.autoFit,
prepend: true
});
this.icon.on('load', this._onImageLoad.bind(this));
this.icon.on('error', this._onImageError.bind(this));
}
protected override _render() {
this.addContainer(this.$parent, 'image-field', new ImageFieldLayout(this));
this.addFieldContainer(this.$parent.makeDiv());
// Complete the layout hierarchy between the image field and the image
let htmlComp = HtmlComponent.install(this.$fieldContainer, this.session);
htmlComp.setLayout(new SingleLayout());
this.icon.render(this.$fieldContainer);
this.addLabel();
this.addField(this.icon.$container);
this.addStatus();
}
protected override _renderProperties() {
super._renderProperties();
this._renderScrollBarEnabled();
this._renderImageUrl();
this._renderUploadEnabled();
}
protected override _remove() {
super._remove();
this._clickHandler = null;
if (this.fileInput) {
this.fileInput.destroy();
this.fileInput = null;
}
}
protected override _getDragAndDropHandlerOptions(): DragAndDropOptions {
let options = super._getDragAndDropHandlerOptions();
options.container = () => this.$fieldContainer;
return options;
}
setImageUrl(imageUrl: string) {
this.setProperty('imageUrl', imageUrl);
}
protected _setImageUrl(imageUrl: string) {
this._setProperty('imageUrl', imageUrl);
this.icon.setIconDesc(imageUrl);
this._updateMenus();
}
protected _renderImageUrl() {
let hasImageUrl = !!this.imageUrl;
this.$fieldContainer.toggleClass('has-image', hasImageUrl);
this.$container.toggleClass('has-image', hasImageUrl);
scrollbars.update(this.$fieldContainer);
}
setAutoFit(autoFit: boolean) {
this.setProperty('autoFit', autoFit);
}
protected _setAutoFit(autoFit: boolean) {
this._setProperty('autoFit', autoFit);
this.icon.setAutoFit(autoFit);
}
protected _renderAutoFit() {
scrollbars.update(this.$fieldContainer);
}
setScrollBarEnabled(scrollBarEnabled: boolean) {
this.setProperty('scrollBarEnabled', scrollBarEnabled);
}
protected _renderScrollBarEnabled() {
// Note: Inner alignment has to be updated _before_ installing the scrollbar, because the inner
// alignment uses absolute positioning, which confuses the scrollbar calculations.
this._updateInnerAlignment();
if (this.scrollBarEnabled) {
this._installScrollbars();
} else {
this._uninstallScrollbars();
}
}
override get$Scrollable(): JQuery {
return this.$fieldContainer;
}
protected override _renderGridData() {
super._renderGridData();
this._updateInnerAlignment();
}
protected override _renderGridDataHints() {
super._renderGridDataHints();
this._updateInnerAlignment();
}
protected _updateInnerAlignment() {
// Enable inner alignment only when scrollbars are disabled
this.updateInnerAlignment({
useHorizontalAlignment: !this.scrollBarEnabled,
useVerticalAlignment: !this.scrollBarEnabled
});
}
protected override _renderEnabled() {
super._renderEnabled();
this._updateUploadEnabled();
}
setUploadEnabled(uploadEnabled: boolean) {
this.setProperty('uploadEnabled', uploadEnabled);
}
protected _renderUploadEnabled() {
this._updateUploadEnabled();
}
protected _updateUploadEnabled() {
let enabled = this.enabledComputed && this.uploadEnabled;
this.$fieldContainer.toggleClass('clickable', enabled);
if (enabled) {
if (!this._clickHandler) {
this._clickHandler = this._onClickUpload.bind(this);
this.$fieldContainer.on('click', this._clickHandler);
}
if (!this.fileInput) {
this.fileInput = scout.create(FileInput, {
parent: this,
acceptTypes: this.acceptTypes,
text: this.displayText,
enabled: this.enabledComputed,
maximumUploadSize: this.maximumUploadSize,
visible: !Device.get().supportsFile()
});
this.fileInput.render(this.$fieldContainer);
this.fileInput.on('change', this._onFileChange.bind(this));
}
} else {
this.$fieldContainer.off('click', this._clickHandler);
this._clickHandler = null;
if (this.fileInput) {
this.fileInput.destroy();
this.fileInput = null;
}
}
}
protected override _getCurrentMenuTypes(): string[] {
if (this.imageUrl) {
return [...super._getCurrentMenuTypes(), ImageField.MenuType.ImageUrl];
}
return [...super._getCurrentMenuTypes(), ImageField.MenuType.Null];
}
/**
* The browse() function triggers an artificial click event on the INPUT element,
* this would trigger our own click handler again. We prevent recursion by
* checking the click target.
*/
protected _onClickUpload(event: JQuery.ClickEvent) {
if ($(event.target).isOrHas(this.$field)) {
this.fileInput.browse();
}
}
protected _onFileChange(event: FileInputChangeEvent) {
this.trigger('fileUpload', {
file: arrays.first(event.files)
});
}
protected _onImageLoad(event: Event<Icon>) {
this._onIconUpdated();
}
protected _onImageError(event: Event<Icon>) {
this._onIconUpdated();
}
/**
* This function is called whenever the icon has updated its $container. Since the $field
* variable from ImageField.js references the $container of the icon directly, we must update
* that variable now.
* <p>
* Override this method if a sub-class of ImageField.js needs to update its DOM too.
*/
protected _onIconUpdated() {
scrollbars.update(this.$fieldContainer);
this.$field = this.icon.$container;
}
}
export type ImageFieldMenuType = EnumObject<typeof ImageField.MenuType>;