UNPKG

@uploadcare/blocks

Version:

Building blocks for Uploadcare products integration

236 lines (209 loc) 6.93 kB
import { UploaderBlock } from '../../abstract/UploaderBlock.js'; import { ActivityBlock } from '../../abstract/ActivityBlock.js'; import { UiConfirmation } from '../ConfirmationDialog/ConfirmationDialog.js'; import { UiMessage } from '../MessageBox/MessageBox.js'; import { EVENT_TYPES, EventData, EventManager } from '../../abstract/EventManager.js'; import { debounce } from '../utils/debounce.js'; export class UploadList extends UploaderBlock { activityType = ActivityBlock.activities.UPLOAD_LIST; init$ = { ...this.ctxInit, doneBtnVisible: false, doneBtnEnabled: false, uploadBtnVisible: false, uploadBtnEnabled: false, addMoreBtnVisible: false, addMoreBtnEnabled: false, hasFiles: false, onAdd: () => { this.initFlow(true); }, onUpload: () => { this.$['*uploadTrigger'] = {}; this._updateUploadsState(); }, onDone: () => { this.cancelFlow(); }, onCancel: () => { let cfn = new UiConfirmation(); cfn.confirmAction = () => { let data = this.getOutputData((dataItem) => { return !!dataItem.getValue('uuid'); }); EventManager.emit( new EventData({ type: EVENT_TYPES.REMOVE, ctx: this.ctxName, data, }) ); this.uploadCollection.clearAll(); this.historyBack(); }; cfn.denyAction = () => { this.historyBack(); }; this.$['*confirmation'] = cfn; }, }; cssInit$ = { '--cfg-show-empty-list': 0, '--cfg-multiple': 1, '--cfg-multiple-min': 0, '--cfg-multiple-max': 0, '--cfg-confirm-upload': 1, '--cfg-source-list': '', }; _debouncedHandleCollectionUpdate = debounce(() => { this._updateUploadsState(); this._updateCountLimitMessage(); }, 0); /** * @private * @returns {{ passed: Boolean; tooFew: Boolean; tooMany: Boolean; exact: Boolean; min: Number; max: Number }} */ _validateFilesCount() { let multiple = !!this.getCssData('--cfg-multiple'); let min = multiple ? this.getCssData('--cfg-multiple-min') ?? 0 : 1; let max = multiple ? this.getCssData('--cfg-multiple-max') ?? 0 : 1; let count = this.uploadCollection.size; let tooFew = min ? count < min : false; let tooMany = max ? count > max : false; let passed = !tooFew && !tooMany; let exact = max === count; return { passed, tooFew, tooMany, min, max, exact, }; } /** @private */ _updateCountLimitMessage() { let filesCount = this.uploadCollection.size; let countValidationResult = this._validateFilesCount(); if (filesCount && !countValidationResult.passed) { let msg = new UiMessage(); let textKey = countValidationResult.tooFew ? 'files-count-limit-error-too-few' : 'files-count-limit-error-too-many'; msg.caption = this.l10n('files-count-limit-error-title'); msg.text = this.l10n(textKey, { min: countValidationResult.min, max: countValidationResult.max, total: filesCount, }); msg.isError = true; this.set$({ '*message': msg, }); } } /** @private */ _updateUploadsState() { let itemIds = this.uploadCollection.items(); let filesCount = itemIds.length; let summary = { total: filesCount, succeed: 0, uploading: 0, failed: 0, }; for (let id of itemIds) { let item = this.uploadCollection.read(id); if (item.getValue('uuid') && !item.getValue('validationErrorMsg')) { summary.succeed += 1; } if (item.getValue('isUploading')) { summary.uploading += 1; } if (item.getValue('validationErrorMsg') || item.getValue('uploadError')) { summary.failed += 1; } } let allDone = summary.total === summary.succeed + summary.failed; let { passed: fitCountRestrictions, tooMany, exact } = this._validateFilesCount(); let fitValidation = summary.failed === 0; let doneBtnEnabled = summary.total > 0 && fitCountRestrictions && fitValidation; let uploadBtnEnabled = summary.total - summary.succeed - summary.uploading - summary.failed > 0 && fitCountRestrictions; this.set$({ doneBtnVisible: allDone, doneBtnEnabled: doneBtnEnabled, uploadBtnVisible: !allDone, uploadBtnEnabled, addMoreBtnEnabled: summary.total === 0 || (!tooMany && !exact), addMoreBtnVisible: !exact || this.getCssData('--cfg-multiple'), }); } initCallback() { super.initCallback(); this.registerActivity(this.activityType, { onActivate: () => { this.set$({ '*activityCaption': this.l10n('selected'), '*activityIcon': 'local', }); }, }); this.sub('--cfg-multiple', this._debouncedHandleCollectionUpdate); this.sub('--cfg-multiple-min', this._debouncedHandleCollectionUpdate); this.sub('--cfg-multiple-max', this._debouncedHandleCollectionUpdate); this.sub('*currentActivity', (currentActivity) => { if ( this.uploadCollection?.size === 0 && !this.getCssData('--cfg-show-empty-list') && currentActivity === this.activityType ) { this.$['*currentActivity'] = this.initActivity; } }); // TODO: could be performance issue on many files // there is no need to update buttons state on every progress tick this.uploadCollection.observe(this._debouncedHandleCollectionUpdate); this.sub('*uploadList', (list) => { this._debouncedHandleCollectionUpdate(); this.set$({ hasFiles: list.length > 0, }); if (list?.length === 0 && !this.getCssData('--cfg-show-empty-list')) { this.cancelFlow(); } }); } destroyCallback() { super.destroyCallback(); this.uploadCollection.unobserve(this._debouncedHandleCollectionUpdate); } } UploadList.template = /* HTML */ ` <div class="no-files" set="@hidden: hasFiles"> <slot name="empty"><span l10n="no-files"></span></slot> </div> <div class="files" repeat="*uploadList" repeat-item-tag="lr-file-item"></div> <div class="toolbar"> <button type="button" class="cancel-btn secondary-btn" set="onclick: onCancel;" l10n="clear"></button> <div class="toolbar-spacer"></div> <button type="button" class="add-more-btn secondary-btn" set="onclick: onAdd; @disabled: !addMoreBtnEnabled; @hidden: !addMoreBtnVisible" l10n="add-more" ></button> <button type="button" class="upload-btn primary-btn" set="@hidden: !uploadBtnVisible; onclick: onUpload; @disabled: !uploadBtnEnabled" l10n="upload" ></button> <button type="button" class="done-btn primary-btn" set="@hidden: !doneBtnVisible; onclick: onDone; @disabled: !doneBtnEnabled" l10n="done" ></button> </div> `;