diff --git a/abstract/ActivityBlock.js b/abstract/ActivityBlock.js index 6878ed45e..36b8eda83 100644 --- a/abstract/ActivityBlock.js +++ b/abstract/ActivityBlock.js @@ -144,6 +144,7 @@ export class ActivityBlock extends Block { if (!hasCurrentActivityInCtx) { this.$['*currentActivity'] = null; + this.setOrAddState('*modalActive', false); } } @@ -199,4 +200,4 @@ ActivityBlock.activities = Object.freeze({ DETAILS: 'details', }); -/** @typedef {(typeof ActivityBlock)['activities'][keyof (typeof ActivityBlock)['activities']] | null} ActivityType */ +/** @typedef {(typeof ActivityBlock)['activities'][keyof (typeof ActivityBlock)['activities']] | (string & {}) | null} ActivityType */ diff --git a/abstract/Block.js b/abstract/Block.js index f4e68c554..ba4b27182 100644 --- a/abstract/Block.js +++ b/abstract/Block.js @@ -118,15 +118,6 @@ export class Block extends BaseComponent { ); } - /** @param {import('./ActivityBlock.js').ActivityType} activityType */ - setActivity(activityType) { - if (this.hasBlockInCtx((b) => b.activityType === activityType)) { - this.$['*currentActivity'] = activityType; - return; - } - console.warn(`Activity type "${activityType}" not found in the context`); - } - connectedCallback() { const styleAttrs = /** @type {typeof Block} */ (this.constructor).styleAttrs; styleAttrs.forEach((attr) => { diff --git a/abstract/CTX.js b/abstract/CTX.js index 92d88c7d3..4462de40b 100644 --- a/abstract/CTX.js +++ b/abstract/CTX.js @@ -6,14 +6,14 @@ export const blockCtx = () => ({}); /** @param {import('./Block').Block} fnCtx */ export const activityBlockCtx = (fnCtx) => ({ ...blockCtx(), - '*currentActivity': '', + '*currentActivity': null, '*currentActivityParams': {}, '*history': [], '*historyBack': null, '*closeModal': () => { fnCtx.set$({ + '*currentActivity': null, '*modalActive': false, - '*currentActivity': '', }); }, }); diff --git a/abstract/UploaderBlock.js b/abstract/UploaderBlock.js index b704cfb96..98e106a3f 100644 --- a/abstract/UploaderBlock.js +++ b/abstract/UploaderBlock.js @@ -107,6 +107,14 @@ export class UploaderBlock extends ActivityBlock { if (!this.$['*secureUploadsManager']) { this.$['*secureUploadsManager'] = new SecureUploadsManager(this); } + + if (this.has('*modalActive')) { + this.sub('*modalActive', (modalActive) => { + if (modalActive && !this.$['*currentActivity']) { + this.$['*modalActive'] = false; + } + }); + } } /** @type {string[]} */ diff --git a/abstract/UploaderPublicApi.js b/abstract/UploaderPublicApi.js index fd17a3261..7373f11bb 100644 --- a/abstract/UploaderPublicApi.js +++ b/abstract/UploaderPublicApi.js @@ -275,4 +275,28 @@ export class UploaderPublicApi { this._ctx.setOrAddState('*modalActive', false); } } + + /** + * @param {import('./ActivityBlock.js').ActivityType} activityType + * @param {import('../blocks/ExternalSource/ExternalSource.js').ActivityParams | {}} [params] + */ + setCurrentActivity(activityType, params = {}) { + if (this._ctx.hasBlockInCtx((b) => b.activityType === activityType)) { + this._ctx.set$({ + '*currentActivityParams': params, + '*currentActivity': activityType, + }); + return; + } + console.warn(`Activity type "${activityType}" not found in the context`); + } + + /** @param {boolean} active */ + setModalActive(active) { + if (active && !this._ctx.$['*currentActivity']) { + console.warn(`Can't open modal without current activity. Please use "setCurrentActivity" method first.`); + return; + } + this._ctx.setOrAddState('*modalActive', active); + } } diff --git a/blocks/ExternalSource/ExternalSource.js b/blocks/ExternalSource/ExternalSource.js index ec7282424..87c7e417d 100644 --- a/blocks/ExternalSource/ExternalSource.js +++ b/blocks/ExternalSource/ExternalSource.js @@ -72,6 +72,13 @@ export class ExternalSource extends UploaderBlock { onActivate: () => { let { externalSourceType } = /** @type {ActivityParams} */ (this.activityParams); + if (!externalSourceType) { + this.$['*currentActivity'] = null; + this.setOrAddState('*modalActive', false); + console.error(`Param "externalSourceType" is required for activity "${this.activityType}"`); + return; + } + this.set$({ activityCaption: `${externalSourceType?.[0].toUpperCase()}${externalSourceType?.slice(1)}`, activityIcon: externalSourceType, @@ -80,6 +87,13 @@ export class ExternalSource extends UploaderBlock { this.mountIframe(); }, }); + this.sub('*currentActivityParams', (val) => { + if (!this.isActivityActive) { + return; + } + this.unmountIframe(); + this.mountIframe(); + }); this.sub('*currentActivity', (val) => { if (val !== this.activityType) { this.unmountIframe(); diff --git a/types/exported.d.ts b/types/exported.d.ts index ba034a1d6..5ea490df0 100644 --- a/types/exported.d.ts +++ b/types/exported.d.ts @@ -3,6 +3,7 @@ import type { complexConfigKeys } from '../blocks/Config/Config'; import type { FuncFileValidator, FuncCollectionValidator } from '../abstract/ValidationManager'; export type { FuncFileValidator, FuncCollectionValidator } from '../abstract/ValidationManager'; +export type { UploaderPublicApi } from '../abstract/UploaderPublicApi'; export type UploadError = import('@uploadcare/upload-client').UploadError; export type UploadcareFile = import('@uploadcare/upload-client').UploadcareFile; diff --git a/types/test/lr-upload-ctx-provider.test-d.tsx b/types/test/lr-upload-ctx-provider.test-d.tsx index ed461bc57..f6d0d0335 100644 --- a/types/test/lr-upload-ctx-provider.test-d.tsx +++ b/types/test/lr-upload-ctx-provider.test-d.tsx @@ -14,11 +14,12 @@ import { useRef } from 'react'; import { UploadcareFile, UploadcareGroup } from '@uploadcare/upload-client'; const instance = new UploadCtxProvider(); - -instance.api.addFileFromUrl('https://example.com/image.png'); instance.uploadCollection.size; instance.setOrAddState('fileId', 'uploading'); +const api = instance.api; +api.addFileFromUrl('https://example.com/image.png'); + instance.addEventListener('change', (e) => { expectType(e); }); @@ -42,7 +43,7 @@ instance.addEventListener('change', (e) => { expectType(state.isFailed); expectType(state.isUploading); expectType<[]>(state.errors); - expectType<'success'>(state.allEntries[0].status) + expectType<'success'>(state.allEntries[0].status); } else if (state.isFailed) { expectType<'failed'>(state.status); expectType(state.isSuccess); @@ -61,7 +62,7 @@ instance.addEventListener('change', (e) => { expectType(state.isFailed); expectType(state.isUploading); expectType<[]>(state.errors); - expectType<'success' | 'idle'>(state.allEntries[0].status) + expectType<'success' | 'idle'>(state.allEntries[0].status); } }); @@ -204,7 +205,9 @@ instance.addEventListener('modal-open', (e) => { instance.addEventListener('activity-change', (e) => { const payload = e.detail; - expectType<(typeof ActivityBlock)['activities'][keyof (typeof ActivityBlock)['activities']] | null>(payload.activity); + expectType<(typeof ActivityBlock)['activities'][keyof (typeof ActivityBlock)['activities']] | null | (string & {})>( + payload.activity, + ); }); () => {