UNPKG

@eclipse-scout/core

Version:
257 lines (217 loc) 7.5 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, 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>;