@atlaskit/media-file-preview 0.0.1 → 0.1.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 CHANGED
@@ -1 +1,7 @@
1
1
  # @atlaskit/media-file-preview
2
+
3
+ ## 0.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#65749](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/65749) [`cf9674e67f0c`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/cf9674e67f0c) - Breaking: updated prop types
@@ -27,7 +27,11 @@ var useFilePreview = exports.useFilePreview = function useFilePreview(_ref) {
27
27
  traceContext = _ref.traceContext,
28
28
  previewDidRender = _ref.previewDidRender,
29
29
  skipRemote = _ref.skipRemote,
30
- mediaBlobUrlAttrs = _ref.mediaBlobUrlAttrs;
30
+ mediaBlobUrlAttrs = _ref.mediaBlobUrlAttrs,
31
+ _ref$allowAnimated = _ref.allowAnimated,
32
+ allowAnimated = _ref$allowAnimated === void 0 ? true : _ref$allowAnimated,
33
+ upscale = _ref.upscale,
34
+ maxAge = _ref.maxAge;
31
35
  var mediaClient = (0, _mediaClientReact.useMediaClient)();
32
36
  var _useState = (0, _react.useState)('loading'),
33
37
  _useState2 = (0, _slicedToArray2.default)(_useState, 2),
@@ -51,17 +55,16 @@ var useFilePreview = exports.useFilePreview = function useFilePreview(_ref) {
51
55
  return dimensions ? (0, _helpers.createRequestDimensions)(dimensions) : undefined;
52
56
  }, [dimensions]);
53
57
  var requestDimensionsRef = (0, _helpers.useCurrentValueRef)(requestDimensions);
54
- var imageURLParams = (0, _react.useMemo)(function () {
55
- return _objectSpread(_objectSpread({
56
- collection: identifier.collectionName,
57
- mode: resizeMode === 'stretchy-fit' ? 'full-fit' : resizeMode
58
- }, requestDimensions), {}, {
59
- allowAnimated: true
60
- });
61
- }, [requestDimensions, identifier.collectionName, resizeMode]);
58
+ var imageURLParams = _objectSpread(_objectSpread({
59
+ collection: identifier.collectionName,
60
+ mode: resizeMode
61
+ }, requestDimensions), {}, {
62
+ allowAnimated: allowAnimated,
63
+ upscale: upscale,
64
+ 'max-age': maxAge
65
+ });
62
66
  var previewInitializer = function previewInitializer() {
63
- var fileImageMode = (0, _mediaClient.imageResizeModeToFileImageMode)(resizeMode);
64
- var preview = _getPreview.mediaFilePreviewCache.get(identifier.id, fileImageMode);
67
+ var preview = _getPreview.mediaFilePreviewCache.get(identifier.id, resizeMode);
65
68
  if (preview) {
66
69
  return preview;
67
70
  }
@@ -227,7 +230,7 @@ var useFilePreview = exports.useFilePreview = function useFilePreview(_ref) {
227
230
  //----------------------------------------------------------------
228
231
 
229
232
  (0, _react.useEffect)(function () {
230
- var cachedPreview = _getPreview.mediaFilePreviewCache.get(identifier.id, imageURLParams.mode);
233
+ var cachedPreview = _getPreview.mediaFilePreviewCache.get(identifier.id, resizeMode);
231
234
 
232
235
  // Cached Preview ----------------------------------------------------------------
233
236
  if (!preview && cachedPreview && !(0, _helpers.isBigger)(cachedPreview === null || cachedPreview === void 0 ? void 0 : cachedPreview.dimensions, requestDimensions)) {
@@ -239,7 +242,7 @@ var useFilePreview = exports.useFilePreview = function useFilePreview(_ref) {
239
242
  // For example, SVGs are mime type NOT supported by browser but media type supported by Media Card (image)
240
243
  // Then, local Preview NOT available
241
244
 
242
- (0, _getPreview.getAndCacheLocalPreview)(identifier.id, fileState.preview, requestDimensions || {}, imageURLParams.mode, mediaBlobUrlAttrs).then(setPreview).catch(function (e) {
245
+ (0, _getPreview.getAndCacheLocalPreview)(identifier.id, fileState.preview, requestDimensions || {}, resizeMode, mediaBlobUrlAttrs).then(setPreview).catch(function (e) {
243
246
  setIsBannedLocalPreview(true);
244
247
  // CXP-2723 TODO: We might have to wrap this error in MediaCardError
245
248
  setNonCriticalError(e);
@@ -252,7 +255,7 @@ var useFilePreview = exports.useFilePreview = function useFilePreview(_ref) {
252
255
  setError((0, _errors.ensureMediaFilePreviewError)('preview-fetch', e));
253
256
  });
254
257
  }
255
- }, [fileState, getAndCacheRemotePreviewRef, identifier.id, imageURLParams.mode, isBannedLocalPreview, mediaBlobUrlAttrs, preview, requestDimensions, skipRemote]);
258
+ }, [fileState, getAndCacheRemotePreviewRef, identifier.id, resizeMode, isBannedLocalPreview, mediaBlobUrlAttrs, preview, requestDimensions, skipRemote]);
256
259
 
257
260
  //----------------------------------------------------------------
258
261
  // RETURN
@@ -289,8 +292,7 @@ var useFilePreview = exports.useFilePreview = function useFilePreview(_ref) {
289
292
  setIsBannedLocalPreview(true);
290
293
  setNonCriticalError(error);
291
294
  }
292
- var fileImageMode = (0, _mediaClient.imageResizeModeToFileImageMode)(resizeMode);
293
- _getPreview.mediaFilePreviewCache.remove(identifier.id, fileImageMode);
295
+ _getPreview.mediaFilePreviewCache.remove(identifier.id, resizeMode);
294
296
  setPreview(undefined);
295
297
  } else {
296
298
  if (!['complete', 'error', 'failed-processing'].includes(status)) {
@@ -1,5 +1,5 @@
1
1
  import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
- import { imageResizeModeToFileImageMode, isImageRepresentationReady } from '@atlaskit/media-client';
2
+ import { isImageRepresentationReady } from '@atlaskit/media-client';
3
3
  import { MediaFileStateError, useFileState, useMediaClient } from '@atlaskit/media-client-react';
4
4
  import { isMimeTypeSupportedByBrowser } from '@atlaskit/media-common';
5
5
  import { extractErrorInfo } from './analytics';
@@ -15,7 +15,10 @@ export const useFilePreview = ({
15
15
  traceContext,
16
16
  previewDidRender,
17
17
  skipRemote,
18
- mediaBlobUrlAttrs
18
+ mediaBlobUrlAttrs,
19
+ allowAnimated = true,
20
+ upscale,
21
+ maxAge
19
22
  }) => {
20
23
  const mediaClient = useMediaClient();
21
24
  const [status, setStatus] = useState('loading');
@@ -26,15 +29,16 @@ export const useFilePreview = ({
26
29
  const ssrReliabilityRef = useRef(initialSsrReliability);
27
30
  const requestDimensions = useMemo(() => dimensions ? createRequestDimensions(dimensions) : undefined, [dimensions]);
28
31
  const requestDimensionsRef = useCurrentValueRef(requestDimensions);
29
- const imageURLParams = useMemo(() => ({
32
+ const imageURLParams = {
30
33
  collection: identifier.collectionName,
31
- mode: resizeMode === 'stretchy-fit' ? 'full-fit' : resizeMode,
34
+ mode: resizeMode,
32
35
  ...requestDimensions,
33
- allowAnimated: true
34
- }), [requestDimensions, identifier.collectionName, resizeMode]);
36
+ allowAnimated,
37
+ upscale,
38
+ 'max-age': maxAge
39
+ };
35
40
  const previewInitializer = () => {
36
- const fileImageMode = imageResizeModeToFileImageMode(resizeMode);
37
- const preview = mediaFilePreviewCache.get(identifier.id, fileImageMode);
41
+ const preview = mediaFilePreviewCache.get(identifier.id, resizeMode);
38
42
  if (preview) {
39
43
  return preview;
40
44
  }
@@ -204,7 +208,7 @@ export const useFilePreview = ({
204
208
  //----------------------------------------------------------------
205
209
 
206
210
  useEffect(() => {
207
- const cachedPreview = mediaFilePreviewCache.get(identifier.id, imageURLParams.mode);
211
+ const cachedPreview = mediaFilePreviewCache.get(identifier.id, resizeMode);
208
212
 
209
213
  // Cached Preview ----------------------------------------------------------------
210
214
  if (!preview && cachedPreview && !isBigger(cachedPreview === null || cachedPreview === void 0 ? void 0 : cachedPreview.dimensions, requestDimensions)) {
@@ -216,7 +220,7 @@ export const useFilePreview = ({
216
220
  // For example, SVGs are mime type NOT supported by browser but media type supported by Media Card (image)
217
221
  // Then, local Preview NOT available
218
222
 
219
- getAndCacheLocalPreview(identifier.id, fileState.preview, requestDimensions || {}, imageURLParams.mode, mediaBlobUrlAttrs).then(setPreview).catch(e => {
223
+ getAndCacheLocalPreview(identifier.id, fileState.preview, requestDimensions || {}, resizeMode, mediaBlobUrlAttrs).then(setPreview).catch(e => {
220
224
  setIsBannedLocalPreview(true);
221
225
  // CXP-2723 TODO: We might have to wrap this error in MediaCardError
222
226
  setNonCriticalError(e);
@@ -229,7 +233,7 @@ export const useFilePreview = ({
229
233
  setError(ensureMediaFilePreviewError('preview-fetch', e));
230
234
  });
231
235
  }
232
- }, [fileState, getAndCacheRemotePreviewRef, identifier.id, imageURLParams.mode, isBannedLocalPreview, mediaBlobUrlAttrs, preview, requestDimensions, skipRemote]);
236
+ }, [fileState, getAndCacheRemotePreviewRef, identifier.id, resizeMode, isBannedLocalPreview, mediaBlobUrlAttrs, preview, requestDimensions, skipRemote]);
233
237
 
234
238
  //----------------------------------------------------------------
235
239
  // RETURN
@@ -267,8 +271,7 @@ export const useFilePreview = ({
267
271
  setIsBannedLocalPreview(true);
268
272
  setNonCriticalError(error);
269
273
  }
270
- const fileImageMode = imageResizeModeToFileImageMode(resizeMode);
271
- mediaFilePreviewCache.remove(identifier.id, fileImageMode);
274
+ mediaFilePreviewCache.remove(identifier.id, resizeMode);
272
275
  setPreview(undefined);
273
276
  } else {
274
277
  if (!['complete', 'error', 'failed-processing'].includes(status)) {
@@ -3,7 +3,7 @@ import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
3
3
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
4
4
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
5
5
  import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
6
- import { imageResizeModeToFileImageMode, isImageRepresentationReady } from '@atlaskit/media-client';
6
+ import { isImageRepresentationReady } from '@atlaskit/media-client';
7
7
  import { MediaFileStateError, useFileState, useMediaClient } from '@atlaskit/media-client-react';
8
8
  import { isMimeTypeSupportedByBrowser } from '@atlaskit/media-common';
9
9
  import { extractErrorInfo } from './analytics';
@@ -20,7 +20,11 @@ export var useFilePreview = function useFilePreview(_ref) {
20
20
  traceContext = _ref.traceContext,
21
21
  previewDidRender = _ref.previewDidRender,
22
22
  skipRemote = _ref.skipRemote,
23
- mediaBlobUrlAttrs = _ref.mediaBlobUrlAttrs;
23
+ mediaBlobUrlAttrs = _ref.mediaBlobUrlAttrs,
24
+ _ref$allowAnimated = _ref.allowAnimated,
25
+ allowAnimated = _ref$allowAnimated === void 0 ? true : _ref$allowAnimated,
26
+ upscale = _ref.upscale,
27
+ maxAge = _ref.maxAge;
24
28
  var mediaClient = useMediaClient();
25
29
  var _useState = useState('loading'),
26
30
  _useState2 = _slicedToArray(_useState, 2),
@@ -44,17 +48,16 @@ export var useFilePreview = function useFilePreview(_ref) {
44
48
  return dimensions ? createRequestDimensions(dimensions) : undefined;
45
49
  }, [dimensions]);
46
50
  var requestDimensionsRef = useCurrentValueRef(requestDimensions);
47
- var imageURLParams = useMemo(function () {
48
- return _objectSpread(_objectSpread({
49
- collection: identifier.collectionName,
50
- mode: resizeMode === 'stretchy-fit' ? 'full-fit' : resizeMode
51
- }, requestDimensions), {}, {
52
- allowAnimated: true
53
- });
54
- }, [requestDimensions, identifier.collectionName, resizeMode]);
51
+ var imageURLParams = _objectSpread(_objectSpread({
52
+ collection: identifier.collectionName,
53
+ mode: resizeMode
54
+ }, requestDimensions), {}, {
55
+ allowAnimated: allowAnimated,
56
+ upscale: upscale,
57
+ 'max-age': maxAge
58
+ });
55
59
  var previewInitializer = function previewInitializer() {
56
- var fileImageMode = imageResizeModeToFileImageMode(resizeMode);
57
- var preview = mediaFilePreviewCache.get(identifier.id, fileImageMode);
60
+ var preview = mediaFilePreviewCache.get(identifier.id, resizeMode);
58
61
  if (preview) {
59
62
  return preview;
60
63
  }
@@ -220,7 +223,7 @@ export var useFilePreview = function useFilePreview(_ref) {
220
223
  //----------------------------------------------------------------
221
224
 
222
225
  useEffect(function () {
223
- var cachedPreview = mediaFilePreviewCache.get(identifier.id, imageURLParams.mode);
226
+ var cachedPreview = mediaFilePreviewCache.get(identifier.id, resizeMode);
224
227
 
225
228
  // Cached Preview ----------------------------------------------------------------
226
229
  if (!preview && cachedPreview && !isBigger(cachedPreview === null || cachedPreview === void 0 ? void 0 : cachedPreview.dimensions, requestDimensions)) {
@@ -232,7 +235,7 @@ export var useFilePreview = function useFilePreview(_ref) {
232
235
  // For example, SVGs are mime type NOT supported by browser but media type supported by Media Card (image)
233
236
  // Then, local Preview NOT available
234
237
 
235
- getAndCacheLocalPreview(identifier.id, fileState.preview, requestDimensions || {}, imageURLParams.mode, mediaBlobUrlAttrs).then(setPreview).catch(function (e) {
238
+ getAndCacheLocalPreview(identifier.id, fileState.preview, requestDimensions || {}, resizeMode, mediaBlobUrlAttrs).then(setPreview).catch(function (e) {
236
239
  setIsBannedLocalPreview(true);
237
240
  // CXP-2723 TODO: We might have to wrap this error in MediaCardError
238
241
  setNonCriticalError(e);
@@ -245,7 +248,7 @@ export var useFilePreview = function useFilePreview(_ref) {
245
248
  setError(ensureMediaFilePreviewError('preview-fetch', e));
246
249
  });
247
250
  }
248
- }, [fileState, getAndCacheRemotePreviewRef, identifier.id, imageURLParams.mode, isBannedLocalPreview, mediaBlobUrlAttrs, preview, requestDimensions, skipRemote]);
251
+ }, [fileState, getAndCacheRemotePreviewRef, identifier.id, resizeMode, isBannedLocalPreview, mediaBlobUrlAttrs, preview, requestDimensions, skipRemote]);
249
252
 
250
253
  //----------------------------------------------------------------
251
254
  // RETURN
@@ -282,8 +285,7 @@ export var useFilePreview = function useFilePreview(_ref) {
282
285
  setIsBannedLocalPreview(true);
283
286
  setNonCriticalError(error);
284
287
  }
285
- var fileImageMode = imageResizeModeToFileImageMode(resizeMode);
286
- mediaFilePreviewCache.remove(identifier.id, fileImageMode);
288
+ mediaFilePreviewCache.remove(identifier.id, resizeMode);
287
289
  setPreview(undefined);
288
290
  } else {
289
291
  if (!['complete', 'error', 'failed-processing'].includes(status)) {
@@ -1,7 +1,7 @@
1
- import { ImageResizeMode } from '@atlaskit/media-client';
1
+ import { MediaStoreGetFileImageParams } from '@atlaskit/media-client';
2
2
  import { MediaFilePreview } from '../types';
3
3
  import { ObjectURLCache } from './objectURLCache';
4
- type Mode = ImageResizeMode | undefined;
4
+ type Mode = MediaStoreGetFileImageParams['mode'] | undefined;
5
5
  export declare const getCacheKey: (id: string, mode: Mode) => string;
6
6
  export interface MediaFilePreviewCache {
7
7
  get(id: string, mode: Mode): MediaFilePreview | undefined;
@@ -1,5 +1,5 @@
1
1
  /// <reference types="react" />
2
- import { FileIdentifier, ImageResizeMode, MediaBlobUrlAttrs } from '@atlaskit/media-client';
2
+ import { FileIdentifier, MediaBlobUrlAttrs, MediaStoreGetFileImageParams } from '@atlaskit/media-client';
3
3
  import { MediaTraceContext, SSR } from '@atlaskit/media-common';
4
4
  import { SSRStatus } from './analytics';
5
5
  import { MediaFilePreviewError } from './errors';
@@ -7,8 +7,8 @@ import { MediaFilePreview, MediaFilePreviewDimensions } from './types';
7
7
  export interface UseFilePreviewParams {
8
8
  /** Instance of file identifier. */
9
9
  readonly identifier: FileIdentifier;
10
- /** Resize the media to 'crop' | 'fit' | 'full-fit' | 'stretchy-fit' */
11
- readonly resizeMode?: ImageResizeMode;
10
+ /** Resize the media to 'crop' | 'fit' | 'full-fit' */
11
+ readonly resizeMode?: MediaStoreGetFileImageParams['mode'];
12
12
  /** Dimensions to be requested to the server. Will be scaled x2 in Retina Displays */
13
13
  readonly dimensions?: MediaFilePreviewDimensions;
14
14
  /** Server-Side-Rendering modes are "server" and "client" */
@@ -21,8 +21,15 @@ export interface UseFilePreviewParams {
21
21
  readonly previewDidRender?: boolean;
22
22
  /** Do not fetch a remote preview. Helpful for lazy loading */
23
23
  readonly skipRemote?: boolean;
24
+ /** Define whether an animated image is acceptable to return */
25
+ readonly allowAnimated?: boolean;
26
+ /** Define the upscale strategy for this image. */
27
+ readonly upscale?: boolean;
28
+ /** Make the client receive the response with the given max-age cache control header. Minimum: 0, maximum: 9223372036854776000.
29
+ */
30
+ readonly maxAge?: number;
24
31
  }
25
- export declare const useFilePreview: ({ resizeMode, identifier, ssr, dimensions, traceContext, previewDidRender, skipRemote, mediaBlobUrlAttrs, }: UseFilePreviewParams) => {
32
+ export declare const useFilePreview: ({ resizeMode, identifier, ssr, dimensions, traceContext, previewDidRender, skipRemote, mediaBlobUrlAttrs, allowAnimated, upscale, maxAge, }: UseFilePreviewParams) => {
26
33
  preview: MediaFilePreview | undefined;
27
34
  error: MediaFilePreviewError | undefined;
28
35
  nonCriticalError: MediaFilePreviewError | undefined;
@@ -1,7 +1,7 @@
1
- import { ImageResizeMode } from '@atlaskit/media-client';
1
+ import { MediaStoreGetFileImageParams } from '@atlaskit/media-client';
2
2
  import { MediaFilePreview } from '../types';
3
3
  import { ObjectURLCache } from './objectURLCache';
4
- type Mode = ImageResizeMode | undefined;
4
+ type Mode = MediaStoreGetFileImageParams['mode'] | undefined;
5
5
  export declare const getCacheKey: (id: string, mode: Mode) => string;
6
6
  export interface MediaFilePreviewCache {
7
7
  get(id: string, mode: Mode): MediaFilePreview | undefined;
@@ -1,5 +1,5 @@
1
1
  /// <reference types="react" />
2
- import { FileIdentifier, ImageResizeMode, MediaBlobUrlAttrs } from '@atlaskit/media-client';
2
+ import { FileIdentifier, MediaBlobUrlAttrs, MediaStoreGetFileImageParams } from '@atlaskit/media-client';
3
3
  import { MediaTraceContext, SSR } from '@atlaskit/media-common';
4
4
  import { SSRStatus } from './analytics';
5
5
  import { MediaFilePreviewError } from './errors';
@@ -7,8 +7,8 @@ import { MediaFilePreview, MediaFilePreviewDimensions } from './types';
7
7
  export interface UseFilePreviewParams {
8
8
  /** Instance of file identifier. */
9
9
  readonly identifier: FileIdentifier;
10
- /** Resize the media to 'crop' | 'fit' | 'full-fit' | 'stretchy-fit' */
11
- readonly resizeMode?: ImageResizeMode;
10
+ /** Resize the media to 'crop' | 'fit' | 'full-fit' */
11
+ readonly resizeMode?: MediaStoreGetFileImageParams['mode'];
12
12
  /** Dimensions to be requested to the server. Will be scaled x2 in Retina Displays */
13
13
  readonly dimensions?: MediaFilePreviewDimensions;
14
14
  /** Server-Side-Rendering modes are "server" and "client" */
@@ -21,8 +21,15 @@ export interface UseFilePreviewParams {
21
21
  readonly previewDidRender?: boolean;
22
22
  /** Do not fetch a remote preview. Helpful for lazy loading */
23
23
  readonly skipRemote?: boolean;
24
+ /** Define whether an animated image is acceptable to return */
25
+ readonly allowAnimated?: boolean;
26
+ /** Define the upscale strategy for this image. */
27
+ readonly upscale?: boolean;
28
+ /** Make the client receive the response with the given max-age cache control header. Minimum: 0, maximum: 9223372036854776000.
29
+ */
30
+ readonly maxAge?: number;
24
31
  }
25
- export declare const useFilePreview: ({ resizeMode, identifier, ssr, dimensions, traceContext, previewDidRender, skipRemote, mediaBlobUrlAttrs, }: UseFilePreviewParams) => {
32
+ export declare const useFilePreview: ({ resizeMode, identifier, ssr, dimensions, traceContext, previewDidRender, skipRemote, mediaBlobUrlAttrs, allowAnimated, upscale, maxAge, }: UseFilePreviewParams) => {
26
33
  preview: MediaFilePreview | undefined;
27
34
  error: MediaFilePreviewError | undefined;
28
35
  nonCriticalError: MediaFilePreviewError | undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/media-file-preview",
3
- "version": "0.0.1",
3
+ "version": "0.1.0",
4
4
  "description": "A React Hook to fetch and render file previews. It's overloaded with fancy features like SSR, lazy loading, memory cache and local preview.",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -52,7 +52,6 @@
52
52
  "@atlaskit/section-message": "^6.4.17",
53
53
  "@atlaskit/ssr": "*",
54
54
  "@atlaskit/visual-regression": "*",
55
- "@atlaskit/webdriver-runner": "*",
56
55
  "@atlassian/atlassian-frontend-prettier-config-1.0.0": "npm:@atlassian/atlassian-frontend-prettier-config@1.0.0",
57
56
  "@testing-library/react": "^12.1.5",
58
57
  "react-dom": "^16.8.0",