@uploadcare/file-uploader 1.9.0 → 1.11.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/blocks/CameraSource/CameraSource.d.ts.map +1 -1
  2. package/blocks/CameraSource/CameraSource.js +0 -2
  3. package/blocks/CameraSource/camera-source.css +4 -15
  4. package/blocks/CloudImageEditorActivity/index.css +3 -3
  5. package/blocks/Config/initialConfig.d.ts +1 -1
  6. package/blocks/Config/initialConfig.d.ts.map +1 -1
  7. package/blocks/Config/initialConfig.js +1 -1
  8. package/blocks/ExternalSource/ExternalSource.d.ts +20 -50
  9. package/blocks/ExternalSource/ExternalSource.d.ts.map +1 -1
  10. package/blocks/ExternalSource/ExternalSource.js +96 -99
  11. package/blocks/ExternalSource/MessageBridge.d.ts +20 -0
  12. package/blocks/ExternalSource/MessageBridge.d.ts.map +1 -0
  13. package/blocks/ExternalSource/MessageBridge.js +71 -0
  14. package/blocks/ExternalSource/buildThemeDefinition.d.ts +3 -0
  15. package/blocks/ExternalSource/buildThemeDefinition.d.ts.map +1 -0
  16. package/blocks/ExternalSource/buildThemeDefinition.js +47 -0
  17. package/blocks/ExternalSource/external-source.css +37 -22
  18. package/blocks/ExternalSource/types.d.ts +113 -0
  19. package/blocks/ExternalSource/types.d.ts.map +1 -0
  20. package/blocks/ExternalSource/types.js +143 -0
  21. package/blocks/Modal/Modal.d.ts.map +1 -1
  22. package/blocks/Modal/Modal.js +4 -11
  23. package/blocks/Modal/modal.css +3 -54
  24. package/blocks/StartFrom/start-from.css +2 -6
  25. package/blocks/UploadList/upload-list.css +1 -11
  26. package/blocks/UrlSource/url-source.css +0 -8
  27. package/blocks/themes/uc-basic/theme.css +3 -3
  28. package/index.ssr.d.ts.map +1 -1
  29. package/index.ssr.js +6 -10
  30. package/locales/file-uploader/en.d.ts +2 -0
  31. package/locales/file-uploader/en.js +3 -1
  32. package/package.json +4 -2
  33. package/web/file-uploader.iife.min.js +4 -4
  34. package/web/file-uploader.min.js +4 -4
  35. package/web/uc-basic.min.css +1 -1
  36. package/web/uc-cloud-image-editor.min.css +1 -1
  37. package/web/uc-cloud-image-editor.min.js +1 -1
  38. package/web/uc-file-uploader-inline.min.css +1 -1
  39. package/web/uc-file-uploader-inline.min.js +4 -4
  40. package/web/uc-file-uploader-minimal.min.css +1 -1
  41. package/web/uc-file-uploader-minimal.min.js +2 -2
  42. package/web/uc-file-uploader-regular.min.css +1 -1
  43. package/web/uc-file-uploader-regular.min.js +4 -4
  44. package/blocks/ExternalSource/buildStyles.d.ts +0 -27
  45. package/blocks/ExternalSource/buildStyles.d.ts.map +0 -1
  46. package/blocks/ExternalSource/buildStyles.js +0 -133
  47. package/blocks/ExternalSource/messages.d.ts +0 -3
  48. package/blocks/ExternalSource/messages.d.ts.map +0 -1
  49. package/blocks/ExternalSource/messages.js +0 -35
@@ -1 +1 @@
1
- {"version":3,"file":"CameraSource.d.ts","sourceRoot":"","sources":["CameraSource.js"],"names":[],"mappings":"AAMA;IAEE,uBAA+C;IAE/C,eAAe;IACf,0BAAyB;IAEzB,WA0BE;IAbE,qBAAqB;IACrB,sCAAuC;IAc3C,eAAe;IACf,oBAKE;IAEF,eAAe;IACf,sBAME;IAEF,eAAe;IACf,iCAEE;IAEF;;;OAGG;IACH,6BA2BQ;IAER,eAAe;IACf,8BASC;IAED,eAAe;IACf,iBAuCC;IAnBC,eAAe;IACf,gBAA+C;IAC/C,eAAe;IACf,aAAyC;IASvC,eAAe;IACf,mBAAsB;IAQ1B,eAAe;IACf,qBAMC;IAED,eAAe;IACf,cAkBC;IAED,8BAgCC;CACF;;;;8BArN6B,iCAAiC"}
1
+ {"version":3,"file":"CameraSource.d.ts","sourceRoot":"","sources":["CameraSource.js"],"names":[],"mappings":"AAMA;IAEE,uBAA+C;IAE/C,eAAe;IACf,0BAAyB;IAEzB,WA0BE;IAbE,qBAAqB;IACrB,sCAAuC;IAc3C,eAAe;IACf,oBAKE;IAEF,eAAe;IACf,sBAME;IAEF,eAAe;IACf,iCAEE;IAEF;;;OAGG;IACH,6BAyBQ;IAER,eAAe;IACf,8BASC;IAED,eAAe;IACf,iBAuCC;IAnBC,eAAe;IACf,gBAA+C;IAC/C,eAAe;IACf,aAAyC;IASvC,eAAe;IACf,mBAAsB;IAQ1B,eAAe;IACf,qBAMC;IAED,eAAe;IACf,cAkBC;IAED,8BAgCC;CACF;;;;8BAnN6B,iCAAiC"}
@@ -66,8 +66,6 @@ export class CameraSource extends UploaderBlock {
66
66
  * @param {'granted' | 'denied' | 'prompt'} state
67
67
  */
68
68
  _setPermissionsState = debounce((state) => {
69
- this.classList.toggle('uc-initialized', state === 'granted');
70
-
71
69
  if (state === 'granted') {
72
70
  this.set$({
73
71
  videoHidden: false,
@@ -10,21 +10,9 @@ uc-camera-source {
10
10
  border-radius: var(--uc-radius);
11
11
  }
12
12
 
13
- [uc-modal] uc-camera-source {
14
- width: min(calc(var(--uc-dialog-max-width) - var(--uc-padding) * 2), calc(100vw - var(--uc-padding) * 2));
15
- height: 100vh;
16
- max-height: var(--modal-max-content-height);
17
- }
18
-
19
- uc-camera-source.uc-initialized {
20
- height: max-content;
21
- }
22
-
23
- @media only screen and (max-width: 430px) {
24
- uc-camera-source {
25
- width: calc(100vw - var(--uc-padding) * 2);
26
- height: var(--modal-content-height-fill, 100%);
27
- }
13
+ [uc-modal] > dialog:has(uc-camera-source[active]) {
14
+ width: 100%;
15
+ height: 100%;
28
16
  }
29
17
 
30
18
  uc-camera-source video {
@@ -52,6 +40,7 @@ uc-camera-source .uc-content {
52
40
  flex: 1;
53
41
  justify-content: center;
54
42
  width: 100%;
43
+ height: 100%;
55
44
  padding: var(--uc-padding);
56
45
  padding-top: 0;
57
46
  overflow: hidden;
@@ -7,7 +7,7 @@ uc-cloud-image-editor-activity {
7
7
  background-color: var(--uc-background);
8
8
  }
9
9
 
10
- [uc-modal] uc-cloud-image-editor-activity {
11
- width: min(calc(var(--uc-dialog-max-width) - var(--uc-padding) * 2), calc(100vw - var(--uc-padding) * 2));
12
- height: var(--modal-content-height-fill, 100%);
10
+ [uc-modal] > dialog:has(uc-cloud-image-editor-activity[active]) {
11
+ width: 100%;
12
+ height: 100%;
13
13
  }
@@ -1,6 +1,6 @@
1
1
  export const DEFAULT_CDN_CNAME: "https://ucarecdn.com";
2
2
  export const DEFAULT_BASE_URL: "https://upload.uploadcare.com";
3
- export const DEFAULT_SOCIAL_BASE_URL: "https://social.uploadcare.com";
3
+ export const DEFAULT_SOCIAL_BASE_URL: "https://social.staging0.uploadcare.com";
4
4
  /** @type {import('../../types/exported').ConfigType} */
5
5
  export const initialConfig: import('../../types/exported').ConfigType;
6
6
  //# sourceMappingURL=initialConfig.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"initialConfig.d.ts","sourceRoot":"","sources":["initialConfig.js"],"names":[],"mappings":"AAKA,uDAAwD;AACxD,+DAAgE;AAChE,sEAAuE;AAEvE,wDAAwD;AACxD,4BADW,OAAO,sBAAsB,EAAE,UAAU,CA2DlD"}
1
+ {"version":3,"file":"initialConfig.d.ts","sourceRoot":"","sources":["initialConfig.js"],"names":[],"mappings":"AAKA,uDAAwD;AACxD,+DAAgE;AAChE,+EAAgF;AAEhF,wDAAwD;AACxD,4BADW,OAAO,sBAAsB,EAAE,UAAU,CA2DlD"}
@@ -5,7 +5,7 @@ import { serializeCsv } from '../utils/comma-separated.js';
5
5
 
6
6
  export const DEFAULT_CDN_CNAME = 'https://ucarecdn.com';
7
7
  export const DEFAULT_BASE_URL = 'https://upload.uploadcare.com';
8
- export const DEFAULT_SOCIAL_BASE_URL = 'https://social.uploadcare.com';
8
+ export const DEFAULT_SOCIAL_BASE_URL = 'https://social.staging0.uploadcare.com';
9
9
 
10
10
  /** @type {import('../../types/exported').ConfigType} */
11
11
  export const initialConfig = {
@@ -1,30 +1,21 @@
1
1
  /** @typedef {{ externalSourceType: string }} ActivityParams */
2
- /**
3
- * @typedef {{
4
- * type: 'file-selected';
5
- * obj_type: 'selected_file';
6
- * filename: string;
7
- * url: string;
8
- * alternatives?: Record<string, string>;
9
- * }} SelectedFileMessage
10
- */
11
- /**
12
- * @typedef {{
13
- * type: 'embed-css';
14
- * style: string;
15
- * }} EmbedCssMessage
16
- */
17
- /** @typedef {SelectedFileMessage | EmbedCssMessage} Message */
18
2
  export class ExternalSource extends UploaderBlock {
19
3
  activityType: "external";
20
4
  init$: {
21
5
  activityIcon: string;
22
6
  activityCaption: string;
23
- selectedList: never[];
24
- counter: number;
25
- multiple: boolean;
7
+ /** @type {import('./types.js').InputMessageMap['selected-files-change']['selectedFiles']} */
8
+ selectedList: import('./types.js').InputMessageMap['selected-files-change']['selectedFiles'];
9
+ total: number;
10
+ isSelectionReady: boolean;
11
+ couldSelectAll: boolean;
12
+ couldDeselectAll: boolean;
13
+ showSelectionStatus: boolean;
14
+ counterText: string;
26
15
  onDone: () => void;
27
16
  onCancel: () => void;
17
+ onSelectAll: () => void;
18
+ onDeselectAll: () => void;
28
19
  '*commonProgress': number;
29
20
  '*uploadList': never[];
30
21
  '*uploadQueue': import("@uploadcare/upload-client").Queue;
@@ -43,39 +34,30 @@ export class ExternalSource extends UploaderBlock {
43
34
  get activityParams(): ActivityParams;
44
35
  /**
45
36
  * @private
46
- * @type {HTMLIFrameElement | null}
37
+ * @param {NonNullable<import('./types.js').InputMessageMap['selected-files-change']['selectedFiles']>[number]} selectedFile
47
38
  */
48
- private _iframe;
39
+ private extractUrlFromSelectedFile;
49
40
  /**
50
41
  * @private
51
- * @param {SelectedFileMessage} message
42
+ * @param {import('./types.js').InputMessageMap['selected-files-change']} message
52
43
  */
53
- private extractUrlFromMessage;
54
- /**
55
- * @private
56
- * @param {Message} message
57
- */
58
- private sendMessage;
59
- /**
60
- * @private
61
- * @param {SelectedFileMessage} message
62
- */
63
- private handleFileSelected;
44
+ private handleSelectedFilesChange;
64
45
  /** @private */
65
46
  private handleIframeLoad;
66
- /**
67
- * @private
68
- * @param {string} propName
69
- */
70
- private getCssValue;
71
47
  /** @private */
72
48
  private applyStyles;
73
49
  /** @private */
50
+ private setupL10n;
51
+ /** @private */
74
52
  private remoteUrl;
75
53
  /** @private */
76
54
  private mountIframe;
77
55
  /** @private */
56
+ private _messageBridge;
57
+ /** @private */
78
58
  private unmountIframe;
59
+ /** @private */
60
+ private resetSelectionStatus;
79
61
  }
80
62
  export namespace ExternalSource {
81
63
  let template: string;
@@ -83,17 +65,5 @@ export namespace ExternalSource {
83
65
  export type ActivityParams = {
84
66
  externalSourceType: string;
85
67
  };
86
- export type SelectedFileMessage = {
87
- type: 'file-selected';
88
- obj_type: 'selected_file';
89
- filename: string;
90
- url: string;
91
- alternatives?: Record<string, string>;
92
- };
93
- export type EmbedCssMessage = {
94
- type: 'embed-css';
95
- style: string;
96
- };
97
- export type Message = SelectedFileMessage | EmbedCssMessage;
98
68
  import { UploaderBlock } from '../../abstract/UploaderBlock.js';
99
69
  //# sourceMappingURL=ExternalSource.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ExternalSource.d.ts","sourceRoot":"","sources":["ExternalSource.js"],"names":[],"mappings":"AAWA,+DAA+D;AAE/D;;;;;;;;GAQG;AAEH;;;;;GAKG;AAEH,+DAA+D;AAE/D;IAEE,yBAAiD;IAK/C;;;;;;;;;;;;;;;;;;;;;MAoBC;IAGH,6BAA6B;IAC7B,qCAMC;IAED;;;OAGG;IACH,gBAAe;IA2Cf;;;OAGG;IACH,8BAcC;IAED;;;OAGG;IACH,oBAEC;IAED;;;OAGG;IACH,2BAUC;IAED,eAAe;IACf,yBAEC;IAED;;;OAGG;IACH,oBAGC;IAED,eAAe;IACf,oBAkBC;IAED,eAAe;IACf,kBAcC;IAED,eAAe;IACf,oBAsBC;IAED,eAAe;IACf,sBAMC;CACF;;;;6BA5Oa;IAAE,kBAAkB,EAAE,MAAM,CAAA;CAAE;kCAG/B;IACZ,IAAQ,EAAE,eAAe,CAAC;IAC1B,QAAY,EAAE,eAAe,CAAC;IAC9B,QAAY,EAAE,MAAM,CAAC;IACrB,GAAO,EAAE,MAAM,CAAC;IAChB,YAAgB,CAAC,EAAE,OAAO,MAAM,EAAE,MAAM,CAAC,CAAC;CACvC;8BAIS;IACZ,IAAQ,EAAE,WAAW,CAAC;IACtB,KAAS,EAAE,MAAM,CAAC;CACf;sBAGU,mBAAmB,GAAG,eAAe;8BA1BrB,iCAAiC"}
1
+ {"version":3,"file":"ExternalSource.d.ts","sourceRoot":"","sources":["ExternalSource.js"],"names":[],"mappings":"AAWA,+DAA+D;AAE/D;IAEE,yBAAiD;IAK/C;;;QAKE,6FAA6F;sBAAlF,OAAO,YAAY,EAAE,eAAe,CAAC,uBAAuB,CAAC,CAAC,eAAe,CAAC;;;;;;;;;;;;;;;;;;;;;;;;MA+B1F;IAGH,6BAA6B;IAC7B,qCAMC;IA4CD;;;OAGG;IACH,mCAcC;IAED;;;OAGG;IACH,kCAoBC;IAED,eAAe;IACf,yBAGC;IAED,eAAe;IACf,oBAKC;IAED,eAAe;IACf,kBAKC;IAED,eAAe;IACf,kBAeC;IAED,eAAe;IACf,oBA6BC;IALC,eAAe;IACf,uBAA6D;IAM/D,eAAe;IACf,sBAMC;IAED,eAAe;IACf,6BASC;CACF;;;;6BA7Oa;IAAE,kBAAkB,EAAE,MAAM,CAAA;CAAE;8BAPd,iCAAiC"}
@@ -5,31 +5,12 @@ import { ActivityBlock } from '../../abstract/ActivityBlock.js';
5
5
  import { UploaderBlock } from '../../abstract/UploaderBlock.js';
6
6
  import { stringToArray } from '../../utils/stringToArray.js';
7
7
  import { wildcardRegexp } from '../../utils/wildcardRegexp.js';
8
- import { buildStyles } from './buildStyles.js';
9
- import { registerMessage, unregisterMessage } from './messages.js';
8
+ import { buildThemeDefinition } from './buildThemeDefinition.js';
9
+ import { MessageBridge } from './MessageBridge.js';
10
10
  import { queryString } from './query-string.js';
11
11
 
12
12
  /** @typedef {{ externalSourceType: string }} ActivityParams */
13
13
 
14
- /**
15
- * @typedef {{
16
- * type: 'file-selected';
17
- * obj_type: 'selected_file';
18
- * filename: string;
19
- * url: string;
20
- * alternatives?: Record<string, string>;
21
- * }} SelectedFileMessage
22
- */
23
-
24
- /**
25
- * @typedef {{
26
- * type: 'embed-css';
27
- * style: string;
28
- * }} EmbedCssMessage
29
- */
30
-
31
- /** @typedef {SelectedFileMessage | EmbedCssMessage} Message */
32
-
33
14
  export class ExternalSource extends UploaderBlock {
34
15
  couldBeCtxOwner = true;
35
16
  activityType = ActivityBlock.activities.EXTERNAL;
@@ -41,12 +22,20 @@ export class ExternalSource extends UploaderBlock {
41
22
  ...this.init$,
42
23
  activityIcon: '',
43
24
  activityCaption: '',
25
+
26
+ /** @type {import('./types.js').InputMessageMap['selected-files-change']['selectedFiles']} */
44
27
  selectedList: [],
45
- counter: 0,
46
- multiple: false,
28
+ total: 0,
29
+
30
+ isSelectionReady: false,
31
+ couldSelectAll: false,
32
+ couldDeselectAll: false,
33
+ showSelectionStatus: false,
34
+ counterText: '',
35
+
47
36
  onDone: () => {
48
37
  for (const message of this.$.selectedList) {
49
- const url = this.extractUrlFromMessage(message);
38
+ const url = this.extractUrlFromSelectedFile(message);
50
39
  const { filename } = message;
51
40
  const { externalSourceType } = this.activityParams;
52
41
  this.api.addFileFromUrl(url, { fileName: filename, source: externalSourceType });
@@ -57,6 +46,14 @@ export class ExternalSource extends UploaderBlock {
57
46
  onCancel: () => {
58
47
  this.historyBack();
59
48
  },
49
+
50
+ onSelectAll: () => {
51
+ this._messageBridge?.send({ type: 'select-all' });
52
+ },
53
+
54
+ onDeselectAll: () => {
55
+ this._messageBridge?.send({ type: 'deselect-all' });
56
+ },
60
57
  };
61
58
  }
62
59
 
@@ -69,12 +66,6 @@ export class ExternalSource extends UploaderBlock {
69
66
  throw new Error(`External Source activity params not found`);
70
67
  }
71
68
 
72
- /**
73
- * @private
74
- * @type {HTMLIFrameElement | null}
75
- */
76
- _iframe = null;
77
-
78
69
  initCallback() {
79
70
  super.initCallback();
80
71
  this.registerActivity(this.activityType, {
@@ -108,24 +99,25 @@ export class ExternalSource extends UploaderBlock {
108
99
  this.unmountIframe();
109
100
  }
110
101
  });
111
- this.sub('selectedList', (list) => {
112
- this.$.counter = list.length;
113
- });
114
102
  this.subConfigValue('multiple', (multiple) => {
115
- this.$.multiple = multiple;
103
+ this.$.showSelectionStatus = multiple;
104
+ });
105
+
106
+ this.subConfigValue('localeName', (val) => {
107
+ this.setupL10n();
116
108
  });
117
109
  }
118
110
 
119
111
  /**
120
112
  * @private
121
- * @param {SelectedFileMessage} message
113
+ * @param {NonNullable<import('./types.js').InputMessageMap['selected-files-change']['selectedFiles']>[number]} selectedFile
122
114
  */
123
- extractUrlFromMessage(message) {
124
- if (message.alternatives) {
115
+ extractUrlFromSelectedFile(selectedFile) {
116
+ if (selectedFile.alternatives) {
125
117
  const preferredTypes = stringToArray(this.cfg.externalSourcesPreferredTypes);
126
118
  for (const preferredType of preferredTypes) {
127
119
  const regexp = wildcardRegexp(preferredType);
128
- for (const [type, typeUrl] of Object.entries(message.alternatives)) {
120
+ for (const [type, typeUrl] of Object.entries(selectedFile.alternatives)) {
129
121
  if (regexp.test(type)) {
130
122
  return typeUrl;
131
123
  }
@@ -133,81 +125,71 @@ export class ExternalSource extends UploaderBlock {
133
125
  }
134
126
  }
135
127
 
136
- return message.url;
128
+ return selectedFile.url;
137
129
  }
138
130
 
139
131
  /**
140
132
  * @private
141
- * @param {Message} message
133
+ * @param {import('./types.js').InputMessageMap['selected-files-change']} message
142
134
  */
143
- sendMessage(message) {
144
- this._iframe?.contentWindow?.postMessage(JSON.stringify(message), '*');
145
- }
146
-
147
- /**
148
- * @private
149
- * @param {SelectedFileMessage} message
150
- */
151
- async handleFileSelected(message) {
152
- if (!this.$.multiple && this.$.selectedList.length) {
135
+ async handleSelectedFilesChange(message) {
136
+ if (this.cfg.multiple !== message.isMultipleMode) {
137
+ console.error('Multiple mode mismatch');
153
138
  return;
154
139
  }
155
140
 
156
- this.$.selectedList = [...this.$.selectedList, message];
157
-
158
- if (!this.$.multiple) {
159
- this.$.onDone();
160
- }
141
+ this.bindL10n('counterText', () =>
142
+ this.l10n('selected-count', {
143
+ count: message.selectedCount,
144
+ total: message.total,
145
+ }),
146
+ );
147
+
148
+ this.set$({
149
+ isSelectionReady: message.isReady,
150
+ showSelectionStatus: message.isMultipleMode && message.total > 0,
151
+ couldSelectAll: message.selectedCount < message.total,
152
+ couldDeselectAll: message.selectedCount === message.total,
153
+ selectedList: message.selectedFiles,
154
+ });
161
155
  }
162
156
 
163
157
  /** @private */
164
158
  handleIframeLoad() {
165
159
  this.applyStyles();
166
- }
167
-
168
- /**
169
- * @private
170
- * @param {string} propName
171
- */
172
- getCssValue(propName) {
173
- let style = window.getComputedStyle(this);
174
- return style.getPropertyValue(propName).trim();
160
+ this.setupL10n();
175
161
  }
176
162
 
177
163
  /** @private */
178
164
  applyStyles() {
179
- let colors = {
180
- radius: this.getCssValue('--uc-radius'),
181
- backgroundColor: this.getCssValue('--uc-background'),
182
- textColor: this.getCssValue('--uc-foreground'),
183
- secondaryColor: this.getCssValue('--uc-secondary'),
184
- secondaryForegroundColor: this.getCssValue('--uc-secondary-foreground'),
185
- secondaryHover: this.getCssValue('--uc-secondary-hover'),
186
- linkColor: this.getCssValue('--uc-primary'),
187
- linkColorHover: this.getCssValue('--uc-primary-hover'),
188
- fontFamily: this.getCssValue('--uc-font-family'),
189
- fontSize: this.getCssValue('--uc-font-size'),
190
- };
165
+ this._messageBridge?.send({
166
+ type: 'set-theme-definition',
167
+ theme: buildThemeDefinition(this),
168
+ });
169
+ }
191
170
 
192
- this.sendMessage({
193
- type: 'embed-css',
194
- style: buildStyles(colors),
171
+ /** @private */
172
+ setupL10n() {
173
+ this._messageBridge?.send({
174
+ type: 'set-locale-definition',
175
+ localeDefinition: this.cfg.localeName,
195
176
  });
196
177
  }
197
178
 
198
179
  /** @private */
199
180
  remoteUrl() {
200
- const { pubkey, remoteTabSessionKey, socialBaseUrl } = this.cfg;
181
+ const { pubkey, remoteTabSessionKey, socialBaseUrl, multiple } = this.cfg;
201
182
  const { externalSourceType } = this.activityParams;
202
183
  const lang = this.l10n('social-source-lang')?.split('-')?.[0] || 'en';
203
184
  const params = {
204
185
  lang,
205
186
  public_key: pubkey,
206
187
  images_only: false.toString(),
207
- pass_window_open: false,
208
188
  session_key: remoteTabSessionKey,
189
+ wait_for_theme: true,
190
+ multiple: multiple.toString(),
209
191
  };
210
- const url = new URL(`/window3/${externalSourceType}`, socialBaseUrl);
192
+ const url = new URL(`/window4/${externalSourceType}`, socialBaseUrl);
211
193
  url.search = queryString(params);
212
194
  return url.toString();
213
195
  }
@@ -231,31 +213,43 @@ export class ExternalSource extends UploaderBlock {
231
213
  this.ref.iframeWrapper.innerHTML = '';
232
214
  this.ref.iframeWrapper.appendChild(iframe);
233
215
 
234
- registerMessage('file-selected', iframe.contentWindow, this.handleFileSelected.bind(this));
216
+ if (!iframe.contentWindow) {
217
+ return;
218
+ }
219
+
220
+ this._messageBridge?.destroy();
221
+
222
+ /** @private */
223
+ this._messageBridge = new MessageBridge(iframe.contentWindow);
224
+ this._messageBridge.on('selected-files-change', this.handleSelectedFilesChange.bind(this));
235
225
 
236
- this._iframe = iframe;
237
- this.$.selectedList = [];
226
+ this.resetSelectionStatus();
238
227
  }
239
228
 
240
229
  /** @private */
241
230
  unmountIframe() {
242
- this._iframe && unregisterMessage('file-selected', this._iframe.contentWindow);
231
+ this._messageBridge?.destroy();
232
+ this._messageBridge = undefined;
243
233
  this.ref.iframeWrapper.innerHTML = '';
244
- this._iframe = null;
245
- this.$.selectedList = [];
246
- this.$.counter = 0;
234
+
235
+ this.resetSelectionStatus();
236
+ }
237
+
238
+ /** @private */
239
+ resetSelectionStatus() {
240
+ this.set$({
241
+ selectedList: [],
242
+ total: 0,
243
+ isSelectionReady: false,
244
+ couldSelectAll: false,
245
+ couldDeselectAll: false,
246
+ showSelectionStatus: false,
247
+ });
247
248
  }
248
249
  }
249
250
 
250
251
  ExternalSource.template = /* HTML */ `
251
252
  <uc-activity-header>
252
- <button type="button" class="uc-mini-btn" set="onclick: *historyBack" l10n="@title:back">
253
- <uc-icon name="back"></uc-icon>
254
- </button>
255
- <div>
256
- <uc-icon set="@name: activityIcon"></uc-icon>
257
- <span>{{activityCaption}}</span>
258
- </div>
259
253
  <button
260
254
  type="button"
261
255
  class="uc-mini-btn uc-close-btn"
@@ -269,12 +263,15 @@ ExternalSource.template = /* HTML */ `
269
263
  <div ref="iframeWrapper" class="uc-iframe-wrapper"></div>
270
264
  <div class="uc-toolbar">
271
265
  <button type="button" class="uc-cancel-btn uc-secondary-btn" set="onclick: onCancel" l10n="cancel"></button>
272
- <div></div>
273
- <div set="@hidden: !multiple" class="uc-selected-counter"><span l10n="selected-count"></span>{{counter}}</div>
266
+ <div set="@hidden: !showSelectionStatus" class="uc-selection-status-box">
267
+ <span>{{counterText}}</span>
268
+ <button type="button" set="onclick: onSelectAll; @hidden: !couldSelectAll" l10n="select-all"></button>
269
+ <button type="button" set="onclick: onDeselectAll; @hidden: !couldDeselectAll" l10n="deselect-all"></button>
270
+ </div>
274
271
  <button
275
272
  type="button"
276
273
  class="uc-done-btn uc-primary-btn"
277
- set="onclick: onDone; @disabled: !counter"
274
+ set="onclick: onDone; @disabled: !isSelectionReady"
278
275
  l10n="done"
279
276
  ></button>
280
277
  </div>
@@ -0,0 +1,20 @@
1
+ export class MessageBridge {
2
+ /** @param {Window} context */
3
+ constructor(context: Window);
4
+ /** @type {Map<string, Set<import('./types').InputMessageHandler<import('./types').InputMessageType>>>} */
5
+ _handlerMap: Map<string, Set<import('./types').InputMessageHandler<import('./types').InputMessageType>>>;
6
+ /** @type {Window} */
7
+ _context: Window;
8
+ /** @param {MessageEvent} e */
9
+ _handleMessage: (e: MessageEvent) => void;
10
+ /**
11
+ * @template {import('./types').InputMessageType} T
12
+ * @param {T} type
13
+ * @param {import('./types').InputMessageHandler<T>} handler
14
+ */
15
+ on<T extends "selected-files-change">(type: T, handler: import("./types").InputMessageHandler<T>): void;
16
+ /** @param {import('./types').OutputMessage} message */
17
+ send(message: import('./types').OutputMessage): void;
18
+ destroy(): void;
19
+ }
20
+ //# sourceMappingURL=MessageBridge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MessageBridge.d.ts","sourceRoot":"","sources":["MessageBridge.js"],"names":[],"mappings":"AAgBA;IAOE,8BAA8B;IAC9B,qBADY,MAAM,EAKjB;IAXD,0GAA0G;IAC1G,aADW,IAAI,MAAM,EAAE,IAAI,OAAO,SAAS,EAAE,mBAAmB,CAAC,OAAO,SAAS,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAC9E;IAExB,qBAAqB;IACrB,UADW,MAAM,CACR;IAST,8BAA8B;IAC9B,oBADY,YAAY,UAgBtB;IAEF;;;;OAIG;IACH,wGAOC;IAED,uDAAuD;IACvD,cADY,OAAO,SAAS,EAAE,aAAa,QAG1C;IAED,gBAEC;CACF"}
@@ -0,0 +1,71 @@
1
+ /** @type {import('./types').InputMessageType[]} */
2
+ const MESSAGE_TYPE_WHITELIST = ['selected-files-change'];
3
+
4
+ /**
5
+ * @param {unknown} message
6
+ * @returns {message is import("./types").InputMessageMap[import("./types").InputMessageType]}
7
+ */
8
+ const isWhitelistedMessage = (message) => {
9
+ if (!message) return false;
10
+ if (typeof message !== 'object') return false;
11
+ return (
12
+ 'type' in message &&
13
+ MESSAGE_TYPE_WHITELIST.includes(/** @type {import('./types').InputMessageType} */ (message.type))
14
+ );
15
+ };
16
+
17
+ export class MessageBridge {
18
+ /** @type {Map<string, Set<import('./types').InputMessageHandler<import('./types').InputMessageType>>>} */
19
+ _handlerMap = new Map();
20
+
21
+ /** @type {Window} */
22
+ _context;
23
+
24
+ /** @param {Window} context */
25
+ constructor(context) {
26
+ this._context = context;
27
+
28
+ window.addEventListener('message', this._handleMessage);
29
+ }
30
+
31
+ /** @param {MessageEvent} e */
32
+ _handleMessage = (e) => {
33
+ if (e.source !== this._context) {
34
+ return;
35
+ }
36
+ const message = e.data;
37
+ if (!isWhitelistedMessage(message)) {
38
+ return;
39
+ }
40
+
41
+ const handlers = this._handlerMap.get(message.type);
42
+ if (handlers) {
43
+ for (const handler of handlers) {
44
+ handler(message);
45
+ }
46
+ }
47
+ };
48
+
49
+ /**
50
+ * @template {import('./types').InputMessageType} T
51
+ * @param {T} type
52
+ * @param {import('./types').InputMessageHandler<T>} handler
53
+ */
54
+ on(type, handler) {
55
+ const handlers = this._handlerMap.get(type) ?? new Set();
56
+ if (!this._handlerMap.has(type)) {
57
+ this._handlerMap.set(type, handlers);
58
+ }
59
+
60
+ handlers.add(/** @type {import('./types').InputMessageHandler<import('./types').InputMessageType>} */ (handler));
61
+ }
62
+
63
+ /** @param {import('./types').OutputMessage} message */
64
+ send(message) {
65
+ this._context.postMessage(message, '*');
66
+ }
67
+
68
+ destroy() {
69
+ window.removeEventListener('message', this._handleMessage);
70
+ }
71
+ }
@@ -0,0 +1,3 @@
1
+ /** @param {HTMLElement} element */
2
+ export function buildThemeDefinition(element: HTMLElement): Record<keyof import("./types.js").ThemeDefinition, string>;
3
+ //# sourceMappingURL=buildThemeDefinition.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buildThemeDefinition.d.ts","sourceRoot":"","sources":["buildThemeDefinition.js"],"names":[],"mappings":"AAqCA,mCAAmC;AACnC,8CADY,WAAW,8DAStB"}
@@ -0,0 +1,47 @@
1
+ // @ts-check
2
+
3
+ /**
4
+ * @param {HTMLElement} element
5
+ * @param {string} propName
6
+ */
7
+ function getCssValue(element, propName) {
8
+ let style = window.getComputedStyle(element);
9
+ return style.getPropertyValue(propName).trim();
10
+ }
11
+
12
+ const ucCustomProperties = /** @type {(keyof import('./types.js').ThemeDefinition)[]} */ ([
13
+ '--uc-font-family',
14
+ '--uc-font-size',
15
+ '--uc-line-height',
16
+ '--uc-button-size',
17
+ '--uc-preview-size',
18
+ '--uc-input-size',
19
+ '--uc-padding',
20
+ '--uc-radius',
21
+ '--uc-transition',
22
+ '--uc-background',
23
+ '--uc-foreground',
24
+ '--uc-primary',
25
+ '--uc-primary-hover',
26
+ '--uc-primary-transparent',
27
+ '--uc-primary-foreground',
28
+ '--uc-secondary',
29
+ '--uc-secondary-hover',
30
+ '--uc-secondary-foreground',
31
+ '--uc-muted',
32
+ '--uc-muted-foreground',
33
+ '--uc-destructive',
34
+ '--uc-destructive-foreground',
35
+ '--uc-border',
36
+ ]);
37
+
38
+ /** @param {HTMLElement} element */
39
+ export function buildThemeDefinition(element) {
40
+ return ucCustomProperties.reduce((acc, prop) => {
41
+ const value = getCssValue(element, prop);
42
+ if (value) {
43
+ acc[prop] = value;
44
+ }
45
+ return acc;
46
+ }, /** @type {Record<keyof import('./types.js').ThemeDefinition, string>} */ ({}));
47
+ }