@atlaskit/media-card 77.2.3 → 77.3.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.
- package/CHANGELOG.md +6 -0
- package/dist/cjs/card/card.js +1 -1
- package/dist/cjs/card/media-card-analytics-error-boundary.js +1 -1
- package/dist/cjs/card/ui/imageRenderer/imageRenderer.js +6 -3
- package/dist/cjs/card/v2/cardV2.js +18 -1037
- package/dist/cjs/card/v2/externalImageCard.js +299 -0
- package/dist/cjs/card/v2/fileCard.js +1001 -0
- package/dist/cjs/inline/loader.js +1 -1
- package/dist/cjs/utils/ufoExperiences.js +1 -1
- package/dist/cjs/utils/useCurrentValueRef.js +12 -0
- package/dist/cjs/utils/usePrevious.js +14 -0
- package/dist/es2019/card/card.js +1 -1
- package/dist/es2019/card/media-card-analytics-error-boundary.js +1 -1
- package/dist/es2019/card/ui/imageRenderer/imageRenderer.js +6 -3
- package/dist/es2019/card/v2/cardV2.js +17 -1021
- package/dist/es2019/card/v2/externalImageCard.js +261 -0
- package/dist/es2019/card/v2/fileCard.js +881 -0
- package/dist/es2019/inline/loader.js +1 -1
- package/dist/es2019/utils/ufoExperiences.js +1 -1
- package/dist/es2019/utils/useCurrentValueRef.js +6 -0
- package/dist/es2019/utils/usePrevious.js +8 -0
- package/dist/esm/card/card.js +1 -1
- package/dist/esm/card/media-card-analytics-error-boundary.js +1 -1
- package/dist/esm/card/ui/imageRenderer/imageRenderer.js +6 -3
- package/dist/esm/card/v2/cardV2.js +19 -1036
- package/dist/esm/card/v2/externalImageCard.js +289 -0
- package/dist/esm/card/v2/fileCard.js +991 -0
- package/dist/esm/inline/loader.js +1 -1
- package/dist/esm/utils/ufoExperiences.js +1 -1
- package/dist/esm/utils/useCurrentValueRef.js +6 -0
- package/dist/esm/utils/usePrevious.js +8 -0
- package/dist/types/card/v2/cardV2.d.ts +4 -60
- package/dist/types/card/v2/externalImageCard.d.ts +14 -0
- package/dist/types/card/v2/fileCard.d.ts +19 -0
- package/dist/types/utils/useCurrentValueRef.d.ts +2 -0
- package/dist/types/utils/usePrevious.d.ts +1 -0
- package/dist/types-ts4.5/card/v2/cardV2.d.ts +4 -60
- package/dist/types-ts4.5/card/v2/externalImageCard.d.ts +14 -0
- package/dist/types-ts4.5/card/v2/fileCard.d.ts +19 -0
- package/dist/types-ts4.5/utils/useCurrentValueRef.d.ts +2 -0
- package/dist/types-ts4.5/utils/usePrevious.d.ts +1 -0
- package/package.json +2 -2
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
import { globalMediaEventEmitter } from '@atlaskit/media-client';
|
|
2
|
+
import { getRandomHex } from '@atlaskit/media-common';
|
|
3
|
+
import { MediaViewer } from '@atlaskit/media-viewer';
|
|
4
|
+
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
|
5
|
+
import ReactDOM from 'react-dom';
|
|
6
|
+
import { ImageLoadError } from '../../errors';
|
|
7
|
+
import getDocument from '../../utils/document';
|
|
8
|
+
import { generateUniqueId } from '../../utils/generateUniqueId';
|
|
9
|
+
import { getMediaCardCursor } from '../../utils/getMediaCardCursor';
|
|
10
|
+
import { abortUfoExperience, completeUfoExperience, startUfoExperience } from '../../utils/ufoExperiences';
|
|
11
|
+
import { useCurrentValueRef } from '../../utils/useCurrentValueRef';
|
|
12
|
+
import { usePrevious } from '../../utils/usePrevious';
|
|
13
|
+
import { fireCommencedEvent, fireCopiedEvent, fireOperationalEvent, fireScreenEvent } from '../cardAnalytics';
|
|
14
|
+
import { CardViewV2 } from './cardViewV2';
|
|
15
|
+
export const ExternalImageCard = ({
|
|
16
|
+
mediaClient,
|
|
17
|
+
appearance = 'auto',
|
|
18
|
+
resizeMode = 'crop',
|
|
19
|
+
disableOverlay = false,
|
|
20
|
+
// Media Feature Flag defaults are defined in @atlaskit/media-common
|
|
21
|
+
featureFlags = {},
|
|
22
|
+
identifier,
|
|
23
|
+
dimensions,
|
|
24
|
+
contextId,
|
|
25
|
+
alt,
|
|
26
|
+
actions,
|
|
27
|
+
shouldOpenMediaViewer,
|
|
28
|
+
selectable,
|
|
29
|
+
selected,
|
|
30
|
+
testId,
|
|
31
|
+
titleBoxBgColor,
|
|
32
|
+
titleBoxIcon,
|
|
33
|
+
shouldHideTooltip,
|
|
34
|
+
mediaViewerItems,
|
|
35
|
+
onClick,
|
|
36
|
+
onMouseEnter,
|
|
37
|
+
createAnalyticsEvent
|
|
38
|
+
}) => {
|
|
39
|
+
const internalOccurrenceKey = useMemo(() => generateUniqueId(), []);
|
|
40
|
+
const timeElapsedTillCommenced = useMemo(() => performance.now(), []);
|
|
41
|
+
|
|
42
|
+
// Generate unique traceId for file
|
|
43
|
+
const traceContext = useMemo(() => ({
|
|
44
|
+
traceId: getRandomHex(8)
|
|
45
|
+
}), []);
|
|
46
|
+
const [cardElement, setCardElement] = useState(null);
|
|
47
|
+
const fileStateFlagsRef = useRef({
|
|
48
|
+
wasStatusUploading: false,
|
|
49
|
+
wasStatusProcessing: false
|
|
50
|
+
});
|
|
51
|
+
const fireCommencedEventRef = useCurrentValueRef(() => {
|
|
52
|
+
createAnalyticsEvent && fireCommencedEvent(createAnalyticsEvent, fileAttributes, {
|
|
53
|
+
overall: {
|
|
54
|
+
durationSincePageStart: timeElapsedTillCommenced
|
|
55
|
+
}
|
|
56
|
+
}, traceContext);
|
|
57
|
+
startUfoExperience(internalOccurrenceKey);
|
|
58
|
+
});
|
|
59
|
+
const [status, setStatus] = useState('loading-preview');
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
fireCommencedEventRef.current();
|
|
62
|
+
setStatus('loading-preview');
|
|
63
|
+
}, [fireCommencedEventRef, identifier]);
|
|
64
|
+
const cardPreview = useMemo(() => ({
|
|
65
|
+
dataURI: identifier.dataURI,
|
|
66
|
+
orientation: 1,
|
|
67
|
+
source: 'external'
|
|
68
|
+
}), [identifier.dataURI]);
|
|
69
|
+
const metadata = {
|
|
70
|
+
id: identifier.mediaItemType,
|
|
71
|
+
name: identifier.name || identifier.dataURI,
|
|
72
|
+
mediaType: 'image'
|
|
73
|
+
};
|
|
74
|
+
const fileAttributes = {
|
|
75
|
+
fileMediatype: 'image',
|
|
76
|
+
fileId: metadata.id
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// for analytics
|
|
80
|
+
const ssrReliability = {
|
|
81
|
+
server: {
|
|
82
|
+
status: 'unknown'
|
|
83
|
+
},
|
|
84
|
+
client: {
|
|
85
|
+
status: 'unknown'
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
const [previewDidRender, setPreviewDidRender] = useState(false);
|
|
89
|
+
const [error, setError] = useState();
|
|
90
|
+
const [mediaViewerSelectedItem, setMediaViewerSelectedItem] = useState(null);
|
|
91
|
+
|
|
92
|
+
//----------------------------------------------------------------//
|
|
93
|
+
//---------------------- Analytics ------------------------------//
|
|
94
|
+
//----------------------------------------------------------------//
|
|
95
|
+
|
|
96
|
+
const fireOperationalEventRef = useCurrentValueRef(() => {
|
|
97
|
+
const timeElapsedTillEvent = performance.now();
|
|
98
|
+
const durationSinceCommenced = timeElapsedTillEvent - timeElapsedTillCommenced;
|
|
99
|
+
const performanceAttributes = {
|
|
100
|
+
overall: {
|
|
101
|
+
durationSincePageStart: timeElapsedTillEvent,
|
|
102
|
+
durationSinceCommenced
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
createAnalyticsEvent && fireOperationalEvent(createAnalyticsEvent, status, fileAttributes, performanceAttributes, ssrReliability, error, traceContext, undefined);
|
|
106
|
+
completeUfoExperience(internalOccurrenceKey, status, fileAttributes, fileStateFlagsRef.current, ssrReliability, error);
|
|
107
|
+
});
|
|
108
|
+
const fireScreenEventRef = useCurrentValueRef(() => {
|
|
109
|
+
createAnalyticsEvent && fireScreenEvent(createAnalyticsEvent, fileAttributes);
|
|
110
|
+
});
|
|
111
|
+
const fireAbortedEventRef = useCurrentValueRef(() => {
|
|
112
|
+
// UFO won't abort if it's already in a final state (succeeded, failed, aborted, etc)
|
|
113
|
+
abortUfoExperience(internalOccurrenceKey, {
|
|
114
|
+
fileAttributes,
|
|
115
|
+
fileStateFlags: fileStateFlagsRef === null || fileStateFlagsRef === void 0 ? void 0 : fileStateFlagsRef.current,
|
|
116
|
+
ssrReliability: ssrReliability
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
//----------------------------------------------------------------//
|
|
121
|
+
//------------------------ useEffects ----------------------------//
|
|
122
|
+
//----------------------------------------------------------------//
|
|
123
|
+
|
|
124
|
+
useEffect(() => {
|
|
125
|
+
var _getDocument;
|
|
126
|
+
const fireCopiedCardEvent = () => {
|
|
127
|
+
cardElement && createAnalyticsEvent && fireCopiedEvent(createAnalyticsEvent, metadata.id, cardElement);
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
// we add a listener for each of the cards on the page
|
|
131
|
+
// and then check if the triggered listener is from the card
|
|
132
|
+
// that contains a div in current window.getSelection()
|
|
133
|
+
// won't work in IE11
|
|
134
|
+
(_getDocument = getDocument()) === null || _getDocument === void 0 ? void 0 : _getDocument.addEventListener('copy', fireCopiedCardEvent);
|
|
135
|
+
return () => {
|
|
136
|
+
var _getDocument2;
|
|
137
|
+
(_getDocument2 = getDocument()) === null || _getDocument2 === void 0 ? void 0 : _getDocument2.removeEventListener('copy', fireCopiedCardEvent);
|
|
138
|
+
};
|
|
139
|
+
}, [cardElement, createAnalyticsEvent, metadata.id]);
|
|
140
|
+
const prevStatus = usePrevious(status);
|
|
141
|
+
useEffect(() => {
|
|
142
|
+
if (prevStatus !== undefined && status !== prevStatus) {
|
|
143
|
+
fireOperationalEventRef.current();
|
|
144
|
+
}
|
|
145
|
+
}, [fireOperationalEventRef, prevStatus, status]);
|
|
146
|
+
useEffect(() => {
|
|
147
|
+
if (previewDidRender && status === 'loading-preview') {
|
|
148
|
+
setStatus('complete');
|
|
149
|
+
}
|
|
150
|
+
}, [previewDidRender, status]);
|
|
151
|
+
useEffect(() => {
|
|
152
|
+
if (prevStatus !== undefined && status !== prevStatus) {
|
|
153
|
+
if (status === 'complete') {
|
|
154
|
+
fireScreenEventRef.current();
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}, [fireScreenEventRef, prevStatus, status]);
|
|
158
|
+
useEffect(() => {
|
|
159
|
+
return () => {
|
|
160
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
161
|
+
fireAbortedEventRef.current();
|
|
162
|
+
};
|
|
163
|
+
}, [fireAbortedEventRef]);
|
|
164
|
+
|
|
165
|
+
//----------------------------------------------------------------//
|
|
166
|
+
//---------------------- Render Functions ------------------------//
|
|
167
|
+
//----------------------------------------------------------------//
|
|
168
|
+
|
|
169
|
+
const renderMediaViewer = () => {
|
|
170
|
+
if (!mediaViewerSelectedItem) {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
return /*#__PURE__*/ReactDOM.createPortal( /*#__PURE__*/React.createElement(MediaViewer, {
|
|
174
|
+
collectionName: '',
|
|
175
|
+
items: mediaViewerItems || [],
|
|
176
|
+
mediaClientConfig: mediaClient.config,
|
|
177
|
+
selectedItem: mediaViewerSelectedItem,
|
|
178
|
+
onClose: () => {
|
|
179
|
+
setMediaViewerSelectedItem(null);
|
|
180
|
+
},
|
|
181
|
+
contextId: contextId,
|
|
182
|
+
featureFlags: featureFlags
|
|
183
|
+
}), document.body);
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
//----------------------------------------------------------------//
|
|
187
|
+
//-------------------------- RENDER ------------------------------//
|
|
188
|
+
//----------------------------------------------------------------//
|
|
189
|
+
|
|
190
|
+
const mediaCardCursor = getMediaCardCursor(false, !!shouldOpenMediaViewer, status === 'error', !!cardPreview, metadata.mediaType);
|
|
191
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(CardViewV2, {
|
|
192
|
+
status: status,
|
|
193
|
+
error: error,
|
|
194
|
+
mediaItemType: identifier.mediaItemType,
|
|
195
|
+
metadata: metadata,
|
|
196
|
+
cardPreview: cardPreview,
|
|
197
|
+
alt: alt,
|
|
198
|
+
appearance: appearance,
|
|
199
|
+
resizeMode: resizeMode,
|
|
200
|
+
dimensions: dimensions,
|
|
201
|
+
actions: actions,
|
|
202
|
+
selectable: selectable,
|
|
203
|
+
selected: selected,
|
|
204
|
+
onClick: (event, analyticsEvent) => {
|
|
205
|
+
if (onClick) {
|
|
206
|
+
const cardEvent = {
|
|
207
|
+
event,
|
|
208
|
+
mediaItemDetails: metadata
|
|
209
|
+
};
|
|
210
|
+
onClick(cardEvent, analyticsEvent);
|
|
211
|
+
}
|
|
212
|
+
if (shouldOpenMediaViewer) {
|
|
213
|
+
setMediaViewerSelectedItem({
|
|
214
|
+
mediaItemType: 'external-image',
|
|
215
|
+
dataURI: identifier.dataURI,
|
|
216
|
+
name: identifier.name
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
},
|
|
220
|
+
onMouseEnter: event => {
|
|
221
|
+
onMouseEnter === null || onMouseEnter === void 0 ? void 0 : onMouseEnter({
|
|
222
|
+
event,
|
|
223
|
+
mediaItemDetails: metadata
|
|
224
|
+
});
|
|
225
|
+
},
|
|
226
|
+
disableOverlay: disableOverlay,
|
|
227
|
+
onDisplayImage: () => {
|
|
228
|
+
const payloadPart = {
|
|
229
|
+
fileId: identifier.dataURI,
|
|
230
|
+
isUserCollection: false
|
|
231
|
+
};
|
|
232
|
+
globalMediaEventEmitter.emit('media-viewed', {
|
|
233
|
+
viewingLevel: 'minimal',
|
|
234
|
+
...payloadPart
|
|
235
|
+
});
|
|
236
|
+
},
|
|
237
|
+
innerRef: setCardElement,
|
|
238
|
+
testId: testId,
|
|
239
|
+
featureFlags: featureFlags,
|
|
240
|
+
titleBoxBgColor: titleBoxBgColor,
|
|
241
|
+
titleBoxIcon: titleBoxIcon,
|
|
242
|
+
onImageError: newCardPreview => {
|
|
243
|
+
// If the dataURI has been replaced, we can dismiss this error
|
|
244
|
+
if ((newCardPreview === null || newCardPreview === void 0 ? void 0 : newCardPreview.dataURI) !== (cardPreview === null || cardPreview === void 0 ? void 0 : cardPreview.dataURI)) {
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
const error = new ImageLoadError(newCardPreview === null || newCardPreview === void 0 ? void 0 : newCardPreview.source);
|
|
248
|
+
setStatus('error');
|
|
249
|
+
setError(error);
|
|
250
|
+
},
|
|
251
|
+
onImageLoad: newCardPreview => {
|
|
252
|
+
// If the dataURI has been replaced, we can dismiss this callback
|
|
253
|
+
if ((newCardPreview === null || newCardPreview === void 0 ? void 0 : newCardPreview.dataURI) !== (cardPreview === null || cardPreview === void 0 ? void 0 : cardPreview.dataURI)) {
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
setPreviewDidRender(true);
|
|
257
|
+
},
|
|
258
|
+
mediaCardCursor: mediaCardCursor,
|
|
259
|
+
shouldHideTooltip: shouldHideTooltip
|
|
260
|
+
}), mediaViewerSelectedItem ? renderMediaViewer() : null);
|
|
261
|
+
};
|