@ankhorage/zora 6.0.0 → 7.0.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 (126) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +2 -1
  3. package/dist/components/image/Image.d.ts +1 -5
  4. package/dist/components/image/Image.d.ts.map +1 -1
  5. package/dist/components/image/Image.js +79 -8
  6. package/dist/components/image/Image.js.map +1 -1
  7. package/dist/components/image/types.d.ts +4 -2
  8. package/dist/components/image/types.d.ts.map +1 -1
  9. package/dist/components/image/types.js.map +1 -1
  10. package/dist/features/uploader/adapters/inbound/Uploader.d.ts +5 -0
  11. package/dist/features/uploader/adapters/inbound/Uploader.d.ts.map +1 -0
  12. package/dist/features/uploader/adapters/inbound/Uploader.js +274 -0
  13. package/dist/features/uploader/adapters/inbound/Uploader.js.map +1 -0
  14. package/dist/features/uploader/adapters/outbound/pickDocumentOrFileAsync.d.ts +5 -0
  15. package/dist/features/uploader/adapters/outbound/pickDocumentOrFileAsync.d.ts.map +1 -0
  16. package/dist/features/uploader/adapters/outbound/pickDocumentOrFileAsync.js +38 -0
  17. package/dist/features/uploader/adapters/outbound/pickDocumentOrFileAsync.js.map +1 -0
  18. package/dist/features/uploader/adapters/outbound/pickImageOrVideoAsync.d.ts +5 -0
  19. package/dist/features/uploader/adapters/outbound/pickImageOrVideoAsync.d.ts.map +1 -0
  20. package/dist/features/uploader/adapters/outbound/pickImageOrVideoAsync.js +24 -0
  21. package/dist/features/uploader/adapters/outbound/pickImageOrVideoAsync.js.map +1 -0
  22. package/dist/features/uploader/application/ports/outbound/UploadPickerPort.d.ts +9 -0
  23. package/dist/features/uploader/application/ports/outbound/UploadPickerPort.d.ts.map +1 -0
  24. package/dist/features/uploader/application/ports/outbound/UploadPickerPort.js +2 -0
  25. package/dist/features/uploader/application/ports/outbound/UploadPickerPort.js.map +1 -0
  26. package/dist/features/uploader/application/use-cases/pickUploadAssetAsync.d.ts +5 -0
  27. package/dist/features/uploader/application/use-cases/pickUploadAssetAsync.d.ts.map +1 -0
  28. package/dist/features/uploader/application/use-cases/pickUploadAssetAsync.js +6 -0
  29. package/dist/features/uploader/application/use-cases/pickUploadAssetAsync.js.map +1 -0
  30. package/dist/features/uploader/application/use-cases/validateUploadAsset.d.ts +4 -0
  31. package/dist/features/uploader/application/use-cases/validateUploadAsset.d.ts.map +1 -0
  32. package/dist/features/uploader/application/use-cases/validateUploadAsset.js +77 -0
  33. package/dist/features/uploader/application/use-cases/validateUploadAsset.js.map +1 -0
  34. package/dist/features/uploader/composition/createUploadPicker.d.ts +4 -0
  35. package/dist/features/uploader/composition/createUploadPicker.d.ts.map +1 -0
  36. package/dist/features/uploader/composition/createUploadPicker.js +12 -0
  37. package/dist/features/uploader/composition/createUploadPicker.js.map +1 -0
  38. package/dist/features/uploader/meta.d.ts +10 -0
  39. package/dist/features/uploader/meta.d.ts.map +1 -0
  40. package/dist/features/uploader/meta.js +10 -0
  41. package/dist/features/uploader/meta.js.map +1 -0
  42. package/dist/features/uploader/public.d.ts +4 -0
  43. package/dist/features/uploader/public.d.ts.map +1 -0
  44. package/dist/features/uploader/public.js +3 -0
  45. package/dist/features/uploader/public.js.map +1 -0
  46. package/dist/index.d.ts +2 -4
  47. package/dist/index.d.ts.map +1 -1
  48. package/dist/index.js +1 -2
  49. package/dist/index.js.map +1 -1
  50. package/dist/metadata/componentMeta.d.ts.map +1 -1
  51. package/dist/metadata/componentMeta.js +2 -4
  52. package/dist/metadata/componentMeta.js.map +1 -1
  53. package/dist/registry.d.ts.map +1 -1
  54. package/dist/registry.js +2 -4
  55. package/dist/registry.js.map +1 -1
  56. package/dist/types/upload.d.ts +53 -0
  57. package/dist/types/upload.d.ts.map +1 -0
  58. package/dist/types/upload.js +2 -0
  59. package/dist/types/upload.js.map +1 -0
  60. package/package.json +11 -2
  61. package/src/components/image/Image.tsx +136 -8
  62. package/src/components/image/types.ts +6 -2
  63. package/src/features/uploader/adapters/inbound/Uploader.tsx +403 -0
  64. package/src/features/uploader/adapters/outbound/pickDocumentOrFileAsync.ts +50 -0
  65. package/src/features/uploader/adapters/outbound/pickImageOrVideoAsync.ts +30 -0
  66. package/src/features/uploader/application/ports/outbound/UploadPickerPort.ts +10 -0
  67. package/src/features/uploader/application/use-cases/pickUploadAssetAsync.test.ts +48 -0
  68. package/src/features/uploader/application/use-cases/pickUploadAssetAsync.ts +14 -0
  69. package/src/features/uploader/application/use-cases/validateUploadAsset.test.ts +51 -0
  70. package/src/features/uploader/application/use-cases/validateUploadAsset.ts +111 -0
  71. package/src/features/uploader/composition/createUploadPicker.ts +14 -0
  72. package/src/features/uploader/meta.ts +11 -0
  73. package/src/features/uploader/public.test.ts +34 -0
  74. package/src/features/uploader/public.ts +10 -0
  75. package/src/index.ts +9 -12
  76. package/src/metadata/componentMeta.ts +2 -4
  77. package/src/registry.test.ts +1 -1
  78. package/src/registry.ts +2 -4
  79. package/src/showcaseCoverage.test.ts +1 -2
  80. package/src/types/upload.ts +62 -0
  81. package/dist/patterns/image-preview/ImagePreview.d.ts +0 -9
  82. package/dist/patterns/image-preview/ImagePreview.d.ts.map +0 -1
  83. package/dist/patterns/image-preview/ImagePreview.js +0 -46
  84. package/dist/patterns/image-preview/ImagePreview.js.map +0 -1
  85. package/dist/patterns/image-preview/index.d.ts +0 -3
  86. package/dist/patterns/image-preview/index.d.ts.map +0 -1
  87. package/dist/patterns/image-preview/index.js +0 -2
  88. package/dist/patterns/image-preview/index.js.map +0 -1
  89. package/dist/patterns/image-preview/meta.d.ts +0 -9
  90. package/dist/patterns/image-preview/meta.d.ts.map +0 -1
  91. package/dist/patterns/image-preview/meta.js +0 -9
  92. package/dist/patterns/image-preview/meta.js.map +0 -1
  93. package/dist/patterns/image-preview/types.d.ts +0 -36
  94. package/dist/patterns/image-preview/types.d.ts.map +0 -1
  95. package/dist/patterns/image-preview/types.js +0 -2
  96. package/dist/patterns/image-preview/types.js.map +0 -1
  97. package/dist/patterns/image-upload-field/ImageUploadField.d.ts +0 -9
  98. package/dist/patterns/image-upload-field/ImageUploadField.d.ts.map +0 -1
  99. package/dist/patterns/image-upload-field/ImageUploadField.js +0 -222
  100. package/dist/patterns/image-upload-field/ImageUploadField.js.map +0 -1
  101. package/dist/patterns/image-upload-field/index.d.ts +0 -3
  102. package/dist/patterns/image-upload-field/index.d.ts.map +0 -1
  103. package/dist/patterns/image-upload-field/index.js +0 -2
  104. package/dist/patterns/image-upload-field/index.js.map +0 -1
  105. package/dist/patterns/image-upload-field/meta.d.ts +0 -9
  106. package/dist/patterns/image-upload-field/meta.d.ts.map +0 -1
  107. package/dist/patterns/image-upload-field/meta.js +0 -9
  108. package/dist/patterns/image-upload-field/meta.js.map +0 -1
  109. package/dist/patterns/image-upload-field/types.d.ts +0 -35
  110. package/dist/patterns/image-upload-field/types.d.ts.map +0 -1
  111. package/dist/patterns/image-upload-field/types.js +0 -2
  112. package/dist/patterns/image-upload-field/types.js.map +0 -1
  113. package/dist/patterns/image-upload-field/uploadFlow.d.ts +0 -18
  114. package/dist/patterns/image-upload-field/uploadFlow.d.ts.map +0 -1
  115. package/dist/patterns/image-upload-field/uploadFlow.js +0 -106
  116. package/dist/patterns/image-upload-field/uploadFlow.js.map +0 -1
  117. package/src/patterns/image-preview/ImagePreview.tsx +0 -82
  118. package/src/patterns/image-preview/index.ts +0 -2
  119. package/src/patterns/image-preview/meta.ts +0 -10
  120. package/src/patterns/image-preview/types.ts +0 -41
  121. package/src/patterns/image-upload-field/ImageUploadField.tsx +0 -319
  122. package/src/patterns/image-upload-field/index.ts +0 -2
  123. package/src/patterns/image-upload-field/meta.ts +0 -10
  124. package/src/patterns/image-upload-field/types.ts +0 -41
  125. package/src/patterns/image-upload-field/uploadFlow.test.ts +0 -117
  126. package/src/patterns/image-upload-field/uploadFlow.ts +0 -145
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 7.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - 75d39e6: Replace `ImagePreview` and `ImageUploadField` with the canonical `Image` asset renderer and generic `Uploader`, backed by Expo image and document pickers.
8
+
3
9
  ## 6.0.0
4
10
 
5
11
  ### Major Changes
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
 
4
4
  # ZORA
5
5
 
6
- ![license: MIT](././paradox/badges/license.svg) ![npm: v4.5.0](././paradox/badges/npm.svg) ![runtime: bun](././paradox/badges/runtime.svg) ![typescript: strict](././paradox/badges/typescript.svg) ![eslint: checked](././paradox/badges/eslint.svg) ![prettier: checked](././paradox/badges/prettier.svg) ![build: checked](././paradox/badges/build.svg) ![tests: checked](././paradox/badges/tests.svg) ![docs: paradox](././paradox/badges/docs.svg)
6
+ ![license: MIT](././paradox/badges/license.svg) ![npm: v6.0.0](././paradox/badges/npm.svg) ![runtime: bun](././paradox/badges/runtime.svg) ![typescript: strict](././paradox/badges/typescript.svg) ![eslint: checked](././paradox/badges/eslint.svg) ![prettier: checked](././paradox/badges/prettier.svg) ![build: checked](././paradox/badges/build.svg) ![tests: checked](././paradox/badges/tests.svg) ![docs: paradox](././paradox/badges/docs.svg)
7
7
 
8
8
  Opinionated React Native and React Native Web UI kit built on @ankhorage/surface.
9
9
 
@@ -72,3 +72,4 @@ export default function BasicApp() {
72
72
  - [useZoraTheme sequence](././paradox/diagrams/sequences/use-zora-theme.mmd)
73
73
  - [validateField sequence](././paradox/diagrams/sequences/validate-field.mmd)
74
74
  - [validateFields sequence](././paradox/diagrams/sequences/validate-fields.mmd)
75
+ - [validateUploadAsset sequence](././paradox/diagrams/sequences/validate-upload-asset.mmd)
@@ -1,9 +1,5 @@
1
1
  import React from 'react';
2
2
  import type { ImageProps } from './types';
3
- /***
4
- * Displays an image with cross-platform source and fit handling.
5
- *
6
-
7
- */
3
+ /*** Displays an image asset and owns its missing, unrenderable, and failed presentation states. */
8
4
  export declare const Image: (props: ImageProps) => React.ReactElement | null;
9
5
  //# sourceMappingURL=Image.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Image.d.ts","sourceRoot":"","sources":["../../../src/components/image/Image.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAW1C;;;;GAIG;AACH,eAAO,MAAM,KAAK,kDAAiC,CAAC"}
1
+ {"version":3,"file":"Image.d.ts","sourceRoot":"","sources":["../../../src/components/image/Image.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,OAAO,CAAC;AAO1B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAE1C,mGAAmG;AACnG,eAAO,MAAM,KAAK,kDAAiC,CAAC"}
@@ -1,13 +1,84 @@
1
1
  import { Image as SurfaceImage } from '@ankhorage/surface';
2
2
  import React from 'react';
3
+ import { Box } from '../../foundation';
4
+ import { useZoraTheme } from '../../theme/useZoraTheme';
3
5
  import { withZoraThemeScope } from '../../theme/withZoraThemeScope';
4
- function ImageInner({ themeId: _themeId, mode: _mode, interactionPolicy: _interactionPolicy, ...props }) {
5
- return <SurfaceImage {...props}/>;
6
- }
7
- /***
8
- * Displays an image with cross-platform source and fit handling.
9
- *
10
-
11
- */
6
+ import { Icon } from '../icon';
7
+ /*** Displays an image asset and owns its missing, unrenderable, and failed presentation states. */
12
8
  export const Image = withZoraThemeScope(ImageInner);
9
+ /*** Renders the resolved image source or the canonical ZORA image placeholder. */
10
+ function ImageInner({ themeId: _themeId, mode: _mode, interactionPolicy: _interactionPolicy, source, fallbackSource, alt, accessibilityLabel, width, height, aspectRatio, radius, style, testID, onError, ...props }) {
11
+ const { theme } = useZoraTheme();
12
+ const resolvedSource = resolveImageSource(source);
13
+ const sourceKey = resolveImageSourceKey(source);
14
+ const firstFailureKeyRef = React.useRef(null);
15
+ const [failedSourceKey, setFailedSourceKey] = React.useState(null);
16
+ const showPlaceholder = resolvedSource === null || failedSourceKey === sourceKey;
17
+ const resolvedAlt = alt ?? (isUploadAsset(source) ? source.alt : undefined);
18
+ const placeholderLabel = accessibilityLabel ?? resolvedAlt;
19
+ if (showPlaceholder) {
20
+ return (<Box accessibilityLabel={placeholderLabel} accessibilityRole={placeholderLabel ? 'image' : undefined} accessible={Boolean(placeholderLabel)} bg={theme.semantics.neutral.surface} borderColor={theme.semantics.neutral.divider} borderWidth={1} height={height} overflow="hidden" radius={radius} testID={testID} width={width} style={[
21
+ {
22
+ alignItems: 'center',
23
+ aspectRatio: aspectRatio ?? 1,
24
+ justifyContent: 'center',
25
+ },
26
+ style,
27
+ ]}>
28
+ <Icon color={theme.semantics.content.muted} name="image-outline" size={24}/>
29
+ </Box>);
30
+ }
31
+ return (<SurfaceImage {...props} accessibilityLabel={accessibilityLabel} alt={resolvedAlt} aspectRatio={aspectRatio} fallbackSource={fallbackSource} height={height} onError={(event) => {
32
+ onError?.(event);
33
+ if (!sourceKey) {
34
+ return;
35
+ }
36
+ if (fallbackSource && firstFailureKeyRef.current !== sourceKey) {
37
+ firstFailureKeyRef.current = sourceKey;
38
+ return;
39
+ }
40
+ setFailedSourceKey(sourceKey);
41
+ }} radius={radius} source={resolvedSource} style={style} testID={testID} width={width}/>);
42
+ }
43
+ /*** Resolves a generic upload asset into a source understood by the Surface Image primitive. */
44
+ function resolveImageSource(source) {
45
+ if (!isUploadAsset(source)) {
46
+ return source;
47
+ }
48
+ switch (source.kind) {
49
+ case 'local':
50
+ return source.uri;
51
+ case 'url':
52
+ return source.url;
53
+ case 'storage':
54
+ return source.publicUrl ?? null;
55
+ }
56
+ }
57
+ /*** Produces a stable-enough key for tracking image failures across source changes. */
58
+ function resolveImageSourceKey(source) {
59
+ if (source === null || source === undefined) {
60
+ return null;
61
+ }
62
+ if (isUploadAsset(source)) {
63
+ switch (source.kind) {
64
+ case 'local':
65
+ return `local:${source.uri}`;
66
+ case 'url':
67
+ return `url:${source.url}`;
68
+ case 'storage':
69
+ return `storage:${source.bucket}:${source.path}:${source.publicUrl ?? ''}`;
70
+ }
71
+ }
72
+ if (typeof source === 'string' || typeof source === 'number') {
73
+ return `source:${String(source)}`;
74
+ }
75
+ if (!Array.isArray(source) && 'uri' in source && typeof source.uri === 'string') {
76
+ return `source:${source.uri}`;
77
+ }
78
+ return 'source:opaque';
79
+ }
80
+ /*** Identifies the generic ZORA upload asset shape without leaking picker-specific types. */
81
+ function isUploadAsset(source) {
82
+ return (typeof source === 'object' && source !== null && !Array.isArray(source) && 'kind' in source);
83
+ }
13
84
  //# sourceMappingURL=Image.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Image.js","sourceRoot":"","sources":["../../../src/components/image/Image.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,IAAI,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAGpE,SAAS,UAAU,CAAC,EAClB,OAAO,EAAE,QAAQ,EACjB,IAAI,EAAE,KAAK,EACX,iBAAiB,EAAE,kBAAkB,EACrC,GAAG,KAAK,EACG;IACX,OAAO,CAAC,YAAY,CAAC,IAAI,KAAK,CAAC,EAAG,CAAC;AACrC,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG,kBAAkB,CAAC,UAAU,CAAC,CAAC","sourcesContent":["import { Image as SurfaceImage } from '@ankhorage/surface';\nimport React from 'react';\n\nimport { withZoraThemeScope } from '../../theme/withZoraThemeScope';\nimport type { ImageProps } from './types';\n\nfunction ImageInner({\n themeId: _themeId,\n mode: _mode,\n interactionPolicy: _interactionPolicy,\n ...props\n}: ImageProps) {\n return <SurfaceImage {...props} />;\n}\n\n/***\n * Displays an image with cross-platform source and fit handling.\n *\n \n */\nexport const Image = withZoraThemeScope(ImageInner);\n"]}
1
+ {"version":3,"file":"Image.js","sourceRoot":"","sources":["../../../src/components/image/Image.tsx"],"names":[],"mappings":"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","sourcesContent":["import { Image as SurfaceImage, type SurfaceImageSource } from '@ankhorage/surface';\nimport React from 'react';\n\nimport { Box } from '../../foundation';\nimport { useZoraTheme } from '../../theme/useZoraTheme';\nimport { withZoraThemeScope } from '../../theme/withZoraThemeScope';\nimport type { UploadAsset } from '../../types/upload';\nimport { Icon } from '../icon';\nimport type { ImageProps } from './types';\n\n/*** Displays an image asset and owns its missing, unrenderable, and failed presentation states. */\nexport const Image = withZoraThemeScope(ImageInner);\n\n/*** Renders the resolved image source or the canonical ZORA image placeholder. */\nfunction ImageInner({\n themeId: _themeId,\n mode: _mode,\n interactionPolicy: _interactionPolicy,\n source,\n fallbackSource,\n alt,\n accessibilityLabel,\n width,\n height,\n aspectRatio,\n radius,\n style,\n testID,\n onError,\n ...props\n}: ImageProps) {\n const { theme } = useZoraTheme();\n const resolvedSource = resolveImageSource(source);\n const sourceKey = resolveImageSourceKey(source);\n const firstFailureKeyRef = React.useRef<string | null>(null);\n const [failedSourceKey, setFailedSourceKey] = React.useState<string | null>(null);\n const showPlaceholder = resolvedSource === null || failedSourceKey === sourceKey;\n const resolvedAlt = alt ?? (isUploadAsset(source) ? source.alt : undefined);\n const placeholderLabel = accessibilityLabel ?? resolvedAlt;\n\n if (showPlaceholder) {\n return (\n <Box\n accessibilityLabel={placeholderLabel}\n accessibilityRole={placeholderLabel ? 'image' : undefined}\n accessible={Boolean(placeholderLabel)}\n bg={theme.semantics.neutral.surface}\n borderColor={theme.semantics.neutral.divider}\n borderWidth={1}\n height={height}\n overflow=\"hidden\"\n radius={radius}\n testID={testID}\n width={width}\n style={[\n {\n alignItems: 'center',\n aspectRatio: aspectRatio ?? 1,\n justifyContent: 'center',\n },\n style,\n ]}\n >\n <Icon color={theme.semantics.content.muted} name=\"image-outline\" size={24} />\n </Box>\n );\n }\n\n return (\n <SurfaceImage\n {...props}\n accessibilityLabel={accessibilityLabel}\n alt={resolvedAlt}\n aspectRatio={aspectRatio}\n fallbackSource={fallbackSource}\n height={height}\n onError={(event) => {\n onError?.(event);\n\n if (!sourceKey) {\n return;\n }\n\n if (fallbackSource && firstFailureKeyRef.current !== sourceKey) {\n firstFailureKeyRef.current = sourceKey;\n return;\n }\n\n setFailedSourceKey(sourceKey);\n }}\n radius={radius}\n source={resolvedSource}\n style={style}\n testID={testID}\n width={width}\n />\n );\n}\n\n/*** Resolves a generic upload asset into a source understood by the Surface Image primitive. */\nfunction resolveImageSource(source: ImageProps['source']): SurfaceImageSource | null | undefined {\n if (!isUploadAsset(source)) {\n return source;\n }\n\n switch (source.kind) {\n case 'local':\n return source.uri;\n case 'url':\n return source.url;\n case 'storage':\n return source.publicUrl ?? null;\n }\n}\n\n/*** Produces a stable-enough key for tracking image failures across source changes. */\nfunction resolveImageSourceKey(source: ImageProps['source']): string | null {\n if (source === null || source === undefined) {\n return null;\n }\n\n if (isUploadAsset(source)) {\n switch (source.kind) {\n case 'local':\n return `local:${source.uri}`;\n case 'url':\n return `url:${source.url}`;\n case 'storage':\n return `storage:${source.bucket}:${source.path}:${source.publicUrl ?? ''}`;\n }\n }\n\n if (typeof source === 'string' || typeof source === 'number') {\n return `source:${String(source)}`;\n }\n\n if (!Array.isArray(source) && 'uri' in source && typeof source.uri === 'string') {\n return `source:${source.uri}`;\n }\n\n return 'source:opaque';\n}\n\n/*** Identifies the generic ZORA upload asset shape without leaking picker-specific types. */\nfunction isUploadAsset(source: ImageProps['source']): source is UploadAsset {\n return (\n typeof source === 'object' && source !== null && !Array.isArray(source) && 'kind' in source\n );\n}\n"]}
@@ -1,6 +1,8 @@
1
- import type { ImageProps as SurfaceImageProps } from '@ankhorage/surface';
1
+ import type { ImageProps as SurfaceImageProps, SurfaceImageSource } from '@ankhorage/surface';
2
2
  import type { ZoraBaseProps } from '../../theme/ZoraBaseProps';
3
+ import type { UploadAsset } from '../../types/upload';
3
4
  export type { ImageFit, SurfaceImageSource } from '@ankhorage/surface';
4
- export interface ImageProps extends ZoraBaseProps, Omit<SurfaceImageProps, 'mode' | 'themeId'> {
5
+ export interface ImageProps extends ZoraBaseProps, Omit<SurfaceImageProps, 'mode' | 'source' | 'themeId'> {
6
+ source?: SurfaceImageSource | UploadAsset | null;
5
7
  }
6
8
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/components/image/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,IAAI,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAE1E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAE/D,YAAY,EAAE,QAAQ,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAEvE,MAAM,WAAW,UAAW,SAAQ,aAAa,EAAE,IAAI,CAAC,iBAAiB,EAAE,MAAM,GAAG,SAAS,CAAC;CAAG"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/components/image/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,IAAI,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAE9F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEtD,YAAY,EAAE,QAAQ,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAEvE,MAAM,WAAW,UACf,SAAQ,aAAa,EAAE,IAAI,CAAC,iBAAiB,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAC;IAC7E,MAAM,CAAC,EAAE,kBAAkB,GAAG,WAAW,GAAG,IAAI,CAAC;CAClD"}
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/components/image/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { ImageProps as SurfaceImageProps } from '@ankhorage/surface';\n\nimport type { ZoraBaseProps } from '../../theme/ZoraBaseProps';\n\nexport type { ImageFit, SurfaceImageSource } from '@ankhorage/surface';\n\nexport interface ImageProps extends ZoraBaseProps, Omit<SurfaceImageProps, 'mode' | 'themeId'> {}\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/components/image/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { ImageProps as SurfaceImageProps, SurfaceImageSource } from '@ankhorage/surface';\n\nimport type { ZoraBaseProps } from '../../theme/ZoraBaseProps';\nimport type { UploadAsset } from '../../types/upload';\n\nexport type { ImageFit, SurfaceImageSource } from '@ankhorage/surface';\n\nexport interface ImageProps\n extends ZoraBaseProps, Omit<SurfaceImageProps, 'mode' | 'source' | 'themeId'> {\n source?: SurfaceImageSource | UploadAsset | null;\n}\n"]}
@@ -0,0 +1,5 @@
1
+ import React from 'react';
2
+ import type { UploaderProps } from '../../../../types/upload';
3
+ /*** Picks, validates, uploads, previews, and removes one generic file asset. */
4
+ export declare const Uploader: (props: UploaderProps) => React.ReactElement | null;
5
+ //# sourceMappingURL=Uploader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Uploader.d.ts","sourceRoot":"","sources":["../../../../../src/features/uploader/adapters/inbound/Uploader.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAW1B,OAAO,KAAK,EAAe,aAAa,EAAc,MAAM,0BAA0B,CAAC;AAIvF,gFAAgF;AAChF,eAAO,MAAM,QAAQ,qDAAoC,CAAC"}
@@ -0,0 +1,274 @@
1
+ import React from 'react';
2
+ import { Button } from '../../../../components/button';
3
+ import { Icon } from '../../../../components/icon';
4
+ import { Image } from '../../../../components/image';
5
+ import { Modal } from '../../../../components/modal';
6
+ import { Progress } from '../../../../components/progress';
7
+ import { Text } from '../../../../components/text';
8
+ import { Box, Stack } from '../../../../foundation';
9
+ import { FormField } from '../../../../patterns/form-field';
10
+ import { withZoraThemeScope } from '../../../../theme/withZoraThemeScope';
11
+ import { validateUploadAsset } from '../../application/use-cases/validateUploadAsset';
12
+ import { createUploadPicker } from '../../composition/createUploadPicker';
13
+ /*** Picks, validates, uploads, previews, and removes one generic file asset. */
14
+ export const Uploader = withZoraThemeScope(UploaderInner);
15
+ /*** Owns the interactive state for the generic uploader inbound adapter. */
16
+ function UploaderInner({ themeId: _themeId, mode: _mode, testID, value, onChange, label, description, helperText, errorText, type = 'file', accept, maxSizeBytes, required, disabled = false, readOnly = false, validatePicked, onUpload, onRemove, aspectRatio = 1, interactionPolicy, }) {
17
+ const passive = interactionPolicy === 'passive';
18
+ const picker = React.useMemo(() => createUploadPicker(), []);
19
+ const [internalError, setInternalError] = React.useState();
20
+ const [uploading, setUploading] = React.useState(false);
21
+ const [removing, setRemoving] = React.useState(false);
22
+ const [progress, setProgress] = React.useState(null);
23
+ const [previewOpen, setPreviewOpen] = React.useState(false);
24
+ const mountedRef = React.useRef(null);
25
+ const resolvedAccept = resolveUploadAccept(type, accept);
26
+ const actionsDisabled = disabled || readOnly;
27
+ const effectiveError = errorText ?? internalError;
28
+ React.useEffect(() => {
29
+ mountedRef.current = true;
30
+ return () => {
31
+ mountedRef.current = false;
32
+ };
33
+ }, []);
34
+ const isMounted = React.useCallback(() => mountedRef.current === true, []);
35
+ const setProgressSafe = React.useCallback((next) => {
36
+ if (!isMounted()) {
37
+ return;
38
+ }
39
+ setProgress(clampProgress(next));
40
+ }, [isMounted]);
41
+ const handlePick = React.useCallback(async () => {
42
+ if (passive || actionsDisabled || uploading || removing) {
43
+ return;
44
+ }
45
+ setInternalError(undefined);
46
+ try {
47
+ const picked = await picker.pickAsync({ accept: resolvedAccept, type });
48
+ if (!picked || !isMounted()) {
49
+ return;
50
+ }
51
+ const validationError = validateUploadAsset({
52
+ accept: resolvedAccept,
53
+ asset: picked,
54
+ maxSizeBytes,
55
+ validate: validatePicked,
56
+ });
57
+ if (validationError) {
58
+ setInternalError(validationError);
59
+ return;
60
+ }
61
+ onChange(picked);
62
+ if (!onUpload) {
63
+ return;
64
+ }
65
+ setUploading(true);
66
+ setProgressSafe(0);
67
+ try {
68
+ const uploaded = await onUpload(picked, { setProgress: setProgressSafe });
69
+ if (!isMounted()) {
70
+ return;
71
+ }
72
+ onChange(uploaded);
73
+ setUploading(false);
74
+ setProgress(null);
75
+ }
76
+ catch (error) {
77
+ if (!isMounted()) {
78
+ return;
79
+ }
80
+ setInternalError(formatUnknownError(error));
81
+ setUploading(false);
82
+ setProgress(null);
83
+ }
84
+ }
85
+ catch (error) {
86
+ if (isMounted()) {
87
+ setInternalError(formatUnknownError(error));
88
+ }
89
+ }
90
+ }, [
91
+ actionsDisabled,
92
+ isMounted,
93
+ maxSizeBytes,
94
+ onChange,
95
+ onUpload,
96
+ passive,
97
+ picker,
98
+ removing,
99
+ resolvedAccept,
100
+ setProgressSafe,
101
+ type,
102
+ uploading,
103
+ validatePicked,
104
+ ]);
105
+ const handleRemove = React.useCallback(async () => {
106
+ if (passive || actionsDisabled || uploading || removing || !value) {
107
+ return;
108
+ }
109
+ setInternalError(undefined);
110
+ if (!onRemove) {
111
+ onChange(null);
112
+ return;
113
+ }
114
+ setRemoving(true);
115
+ try {
116
+ await onRemove(value);
117
+ if (isMounted()) {
118
+ onChange(null);
119
+ setRemoving(false);
120
+ }
121
+ }
122
+ catch (error) {
123
+ if (isMounted()) {
124
+ setInternalError(formatUnknownError(error));
125
+ setRemoving(false);
126
+ }
127
+ }
128
+ }, [actionsDisabled, isMounted, onChange, onRemove, passive, removing, uploading, value]);
129
+ const canPreviewImage = type === 'image' && resolveRenderableAssetUrl(value) !== null;
130
+ return (<>
131
+ <FormField description={description} disabled={disabled} errorText={effectiveError} helperText={helperText} interactionPolicy={interactionPolicy} invalid={Boolean(effectiveError)} label={label} readOnly={readOnly} required={required} testID={testID}>
132
+ <Stack gap="m">
133
+ <UploadPresentation asset={value} aspectRatio={aspectRatio} type={type}/>
134
+
135
+ {resolvedAccept || maxSizeBytes ? (<Stack gap="xs">
136
+ {resolvedAccept ? (<Text emphasis="muted" variant="caption">
137
+ Accepted: {resolvedAccept}
138
+ </Text>) : null}
139
+ {maxSizeBytes ? (<Text emphasis="muted" variant="caption">
140
+ Max size: {formatBytes(maxSizeBytes)}
141
+ </Text>) : null}
142
+ </Stack>) : null}
143
+
144
+ {uploading ? (<Stack gap="xs">
145
+ <Text emphasis="muted" variant="caption">
146
+ Uploading…
147
+ </Text>
148
+ {progress !== null ? <Progress max={1} value={progress}/> : null}
149
+ </Stack>) : null}
150
+
151
+ <Stack direction={{ base: 'column', md: 'row' }} gap="s">
152
+ <Button disabled={actionsDisabled || uploading || removing} interactionPolicy={interactionPolicy} onPress={() => {
153
+ void handlePick();
154
+ }}>
155
+ {value ? 'Replace file' : 'Select file'}
156
+ </Button>
157
+
158
+ {value ? (<Button color="danger" disabled={actionsDisabled || uploading || removing} interactionPolicy={interactionPolicy} loading={removing} variant="outline" onPress={() => {
159
+ void handleRemove();
160
+ }}>
161
+ Remove
162
+ </Button>) : null}
163
+
164
+ {canPreviewImage ? (<Button color="neutral" interactionPolicy={interactionPolicy} variant="soft" onPress={() => setPreviewOpen(true)}>
165
+ Preview
166
+ </Button>) : null}
167
+ </Stack>
168
+ </Stack>
169
+ </FormField>
170
+
171
+ {canPreviewImage ? (<Modal closeOnBackdrop interactionPolicy={interactionPolicy} onDismiss={() => setPreviewOpen(false)} title="Image preview" visible={previewOpen}>
172
+ <Stack gap="m">
173
+ <Image aspectRatio={aspectRatio} fit="contain" source={value}/>
174
+ <Stack direction="row" justify="flex-end">
175
+ <Button color="neutral" interactionPolicy={interactionPolicy} variant="soft" onPress={() => setPreviewOpen(false)}>
176
+ Close
177
+ </Button>
178
+ </Stack>
179
+ </Stack>
180
+ </Modal>) : null}
181
+ </>);
182
+ }
183
+ /*** Renders image previews or generic file metadata without exposing picker-specific assets. */
184
+ function UploadPresentation({ asset, aspectRatio, type, }) {
185
+ if (type === 'image') {
186
+ return <Image aspectRatio={aspectRatio} source={asset}/>;
187
+ }
188
+ return (<Box borderWidth={1} p="m" radius="m">
189
+ <Stack direction="row" gap="s">
190
+ <Icon name={resolveUploadIcon(type)} size={22}/>
191
+ <Stack gap="xs">
192
+ <Text variant="label" weight="semiBold">
193
+ {asset?.fileName ?? 'No file selected'}
194
+ </Text>
195
+ {asset?.contentType ? (<Text emphasis="muted" variant="caption">
196
+ {asset.contentType}
197
+ </Text>) : null}
198
+ </Stack>
199
+ </Stack>
200
+ </Box>);
201
+ }
202
+ /*** Resolves the native picker default while allowing accept to remain the precise restriction. */
203
+ function resolveUploadAccept(type, accept) {
204
+ if (accept) {
205
+ return accept;
206
+ }
207
+ if (type === 'image') {
208
+ return 'image/*';
209
+ }
210
+ if (type === 'video') {
211
+ return 'video/*';
212
+ }
213
+ return undefined;
214
+ }
215
+ /*** Resolves an upload asset URL when it can be rendered directly. */
216
+ function resolveRenderableAssetUrl(asset) {
217
+ if (!asset) {
218
+ return null;
219
+ }
220
+ switch (asset.kind) {
221
+ case 'local':
222
+ return asset.uri;
223
+ case 'url':
224
+ return asset.url;
225
+ case 'storage':
226
+ return asset.publicUrl ?? null;
227
+ }
228
+ }
229
+ /*** Resolves a generic icon for the selected upload mode. */
230
+ function resolveUploadIcon(type) {
231
+ switch (type) {
232
+ case 'video':
233
+ return 'videocam-outline';
234
+ case 'document':
235
+ return 'document-text-outline';
236
+ case 'file':
237
+ case 'image':
238
+ return 'attach-outline';
239
+ }
240
+ }
241
+ /*** Clamps progress callbacks into the normalized 0..1 range. */
242
+ function clampProgress(value) {
243
+ if (value === null || !Number.isFinite(value)) {
244
+ return null;
245
+ }
246
+ return Math.max(0, Math.min(1, value));
247
+ }
248
+ /*** Converts unknown picker/upload failures into user-facing text. */
249
+ function formatUnknownError(error) {
250
+ if (error instanceof Error && error.message) {
251
+ return error.message;
252
+ }
253
+ if (typeof error === 'string' && error.trim().length > 0) {
254
+ return error;
255
+ }
256
+ return 'Something went wrong.';
257
+ }
258
+ /*** Formats an upload byte limit for concise helper text. */
259
+ function formatBytes(value) {
260
+ if (!Number.isFinite(value) || value <= 0) {
261
+ return '0 B';
262
+ }
263
+ const units = ['B', 'KB', 'MB', 'GB'];
264
+ let size = value;
265
+ let unitIndex = 0;
266
+ while (size >= 1024 && unitIndex < units.length - 1) {
267
+ size /= 1024;
268
+ unitIndex += 1;
269
+ }
270
+ const unit = units.at(unitIndex) ?? 'B';
271
+ const rounded = unitIndex === 0 ? Math.round(size) : Math.round(size * 10) / 10;
272
+ return `${rounded} ${unit}`;
273
+ }
274
+ //# sourceMappingURL=Uploader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Uploader.js","sourceRoot":"","sources":["../../../../../src/features/uploader/adapters/inbound/Uploader.tsx"],"names":[],"mappings":"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","sourcesContent":["import React from 'react';\n\nimport { Button } from '../../../../components/button';\nimport { Icon } from '../../../../components/icon';\nimport { Image } from '../../../../components/image';\nimport { Modal } from '../../../../components/modal';\nimport { Progress } from '../../../../components/progress';\nimport { Text } from '../../../../components/text';\nimport { Box, Stack } from '../../../../foundation';\nimport { FormField } from '../../../../patterns/form-field';\nimport { withZoraThemeScope } from '../../../../theme/withZoraThemeScope';\nimport type { UploadAsset, UploaderProps, UploadType } from '../../../../types/upload';\nimport { validateUploadAsset } from '../../application/use-cases/validateUploadAsset';\nimport { createUploadPicker } from '../../composition/createUploadPicker';\n\n/*** Picks, validates, uploads, previews, and removes one generic file asset. */\nexport const Uploader = withZoraThemeScope(UploaderInner);\n\n/*** Owns the interactive state for the generic uploader inbound adapter. */\nfunction UploaderInner({\n themeId: _themeId,\n mode: _mode,\n testID,\n value,\n onChange,\n label,\n description,\n helperText,\n errorText,\n type = 'file',\n accept,\n maxSizeBytes,\n required,\n disabled = false,\n readOnly = false,\n validatePicked,\n onUpload,\n onRemove,\n aspectRatio = 1,\n interactionPolicy,\n}: UploaderProps) {\n const passive = interactionPolicy === 'passive';\n const picker = React.useMemo(() => createUploadPicker(), []);\n const [internalError, setInternalError] = React.useState<string | undefined>();\n const [uploading, setUploading] = React.useState(false);\n const [removing, setRemoving] = React.useState(false);\n const [progress, setProgress] = React.useState<number | null>(null);\n const [previewOpen, setPreviewOpen] = React.useState(false);\n const mountedRef = React.useRef<boolean | null>(null);\n const resolvedAccept = resolveUploadAccept(type, accept);\n const actionsDisabled = disabled || readOnly;\n const effectiveError = errorText ?? internalError;\n\n React.useEffect(() => {\n mountedRef.current = true;\n\n return () => {\n mountedRef.current = false;\n };\n }, []);\n\n const isMounted = React.useCallback(() => mountedRef.current === true, []);\n\n const setProgressSafe = React.useCallback(\n (next: number | null) => {\n if (!isMounted()) {\n return;\n }\n\n setProgress(clampProgress(next));\n },\n [isMounted],\n );\n\n const handlePick = React.useCallback(async () => {\n if (passive || actionsDisabled || uploading || removing) {\n return;\n }\n\n setInternalError(undefined);\n\n try {\n const picked = await picker.pickAsync({ accept: resolvedAccept, type });\n if (!picked || !isMounted()) {\n return;\n }\n\n const validationError = validateUploadAsset({\n accept: resolvedAccept,\n asset: picked,\n maxSizeBytes,\n validate: validatePicked,\n });\n if (validationError) {\n setInternalError(validationError);\n return;\n }\n\n onChange(picked);\n if (!onUpload) {\n return;\n }\n\n setUploading(true);\n setProgressSafe(0);\n\n try {\n const uploaded = await onUpload(picked, { setProgress: setProgressSafe });\n if (!isMounted()) {\n return;\n }\n\n onChange(uploaded);\n setUploading(false);\n setProgress(null);\n } catch (error) {\n if (!isMounted()) {\n return;\n }\n\n setInternalError(formatUnknownError(error));\n setUploading(false);\n setProgress(null);\n }\n } catch (error) {\n if (isMounted()) {\n setInternalError(formatUnknownError(error));\n }\n }\n }, [\n actionsDisabled,\n isMounted,\n maxSizeBytes,\n onChange,\n onUpload,\n passive,\n picker,\n removing,\n resolvedAccept,\n setProgressSafe,\n type,\n uploading,\n validatePicked,\n ]);\n\n const handleRemove = React.useCallback(async () => {\n if (passive || actionsDisabled || uploading || removing || !value) {\n return;\n }\n\n setInternalError(undefined);\n if (!onRemove) {\n onChange(null);\n return;\n }\n\n setRemoving(true);\n try {\n await onRemove(value);\n if (isMounted()) {\n onChange(null);\n setRemoving(false);\n }\n } catch (error) {\n if (isMounted()) {\n setInternalError(formatUnknownError(error));\n setRemoving(false);\n }\n }\n }, [actionsDisabled, isMounted, onChange, onRemove, passive, removing, uploading, value]);\n\n const canPreviewImage = type === 'image' && resolveRenderableAssetUrl(value) !== null;\n\n return (\n <>\n <FormField\n description={description}\n disabled={disabled}\n errorText={effectiveError}\n helperText={helperText}\n interactionPolicy={interactionPolicy}\n invalid={Boolean(effectiveError)}\n label={label}\n readOnly={readOnly}\n required={required}\n testID={testID}\n >\n <Stack gap=\"m\">\n <UploadPresentation asset={value} aspectRatio={aspectRatio} type={type} />\n\n {resolvedAccept || maxSizeBytes ? (\n <Stack gap=\"xs\">\n {resolvedAccept ? (\n <Text emphasis=\"muted\" variant=\"caption\">\n Accepted: {resolvedAccept}\n </Text>\n ) : null}\n {maxSizeBytes ? (\n <Text emphasis=\"muted\" variant=\"caption\">\n Max size: {formatBytes(maxSizeBytes)}\n </Text>\n ) : null}\n </Stack>\n ) : null}\n\n {uploading ? (\n <Stack gap=\"xs\">\n <Text emphasis=\"muted\" variant=\"caption\">\n Uploading…\n </Text>\n {progress !== null ? <Progress max={1} value={progress} /> : null}\n </Stack>\n ) : null}\n\n <Stack direction={{ base: 'column', md: 'row' }} gap=\"s\">\n <Button\n disabled={actionsDisabled || uploading || removing}\n interactionPolicy={interactionPolicy}\n onPress={() => {\n void handlePick();\n }}\n >\n {value ? 'Replace file' : 'Select file'}\n </Button>\n\n {value ? (\n <Button\n color=\"danger\"\n disabled={actionsDisabled || uploading || removing}\n interactionPolicy={interactionPolicy}\n loading={removing}\n variant=\"outline\"\n onPress={() => {\n void handleRemove();\n }}\n >\n Remove\n </Button>\n ) : null}\n\n {canPreviewImage ? (\n <Button\n color=\"neutral\"\n interactionPolicy={interactionPolicy}\n variant=\"soft\"\n onPress={() => setPreviewOpen(true)}\n >\n Preview\n </Button>\n ) : null}\n </Stack>\n </Stack>\n </FormField>\n\n {canPreviewImage ? (\n <Modal\n closeOnBackdrop\n interactionPolicy={interactionPolicy}\n onDismiss={() => setPreviewOpen(false)}\n title=\"Image preview\"\n visible={previewOpen}\n >\n <Stack gap=\"m\">\n <Image aspectRatio={aspectRatio} fit=\"contain\" source={value} />\n <Stack direction=\"row\" justify=\"flex-end\">\n <Button\n color=\"neutral\"\n interactionPolicy={interactionPolicy}\n variant=\"soft\"\n onPress={() => setPreviewOpen(false)}\n >\n Close\n </Button>\n </Stack>\n </Stack>\n </Modal>\n ) : null}\n </>\n );\n}\n\n/*** Renders image previews or generic file metadata without exposing picker-specific assets. */\nfunction UploadPresentation({\n asset,\n aspectRatio,\n type,\n}: {\n asset: UploadAsset | null;\n aspectRatio: number;\n type: UploadType;\n}) {\n if (type === 'image') {\n return <Image aspectRatio={aspectRatio} source={asset} />;\n }\n\n return (\n <Box borderWidth={1} p=\"m\" radius=\"m\">\n <Stack direction=\"row\" gap=\"s\">\n <Icon name={resolveUploadIcon(type)} size={22} />\n <Stack gap=\"xs\">\n <Text variant=\"label\" weight=\"semiBold\">\n {asset?.fileName ?? 'No file selected'}\n </Text>\n {asset?.contentType ? (\n <Text emphasis=\"muted\" variant=\"caption\">\n {asset.contentType}\n </Text>\n ) : null}\n </Stack>\n </Stack>\n </Box>\n );\n}\n\n/*** Resolves the native picker default while allowing accept to remain the precise restriction. */\nfunction resolveUploadAccept(type: UploadType, accept: string | undefined): string | undefined {\n if (accept) {\n return accept;\n }\n\n if (type === 'image') {\n return 'image/*';\n }\n\n if (type === 'video') {\n return 'video/*';\n }\n\n return undefined;\n}\n\n/*** Resolves an upload asset URL when it can be rendered directly. */\nfunction resolveRenderableAssetUrl(asset: UploadAsset | null): string | null {\n if (!asset) {\n return null;\n }\n\n switch (asset.kind) {\n case 'local':\n return asset.uri;\n case 'url':\n return asset.url;\n case 'storage':\n return asset.publicUrl ?? null;\n }\n}\n\n/*** Resolves a generic icon for the selected upload mode. */\nfunction resolveUploadIcon(\n type: UploadType,\n): 'attach-outline' | 'document-text-outline' | 'videocam-outline' {\n switch (type) {\n case 'video':\n return 'videocam-outline';\n case 'document':\n return 'document-text-outline';\n case 'file':\n case 'image':\n return 'attach-outline';\n }\n}\n\n/*** Clamps progress callbacks into the normalized 0..1 range. */\nfunction clampProgress(value: number | null): number | null {\n if (value === null || !Number.isFinite(value)) {\n return null;\n }\n\n return Math.max(0, Math.min(1, value));\n}\n\n/*** Converts unknown picker/upload failures into user-facing text. */\nfunction formatUnknownError(error: unknown): string {\n if (error instanceof Error && error.message) {\n return error.message;\n }\n\n if (typeof error === 'string' && error.trim().length > 0) {\n return error;\n }\n\n return 'Something went wrong.';\n}\n\n/*** Formats an upload byte limit for concise helper text. */\nfunction formatBytes(value: number): string {\n if (!Number.isFinite(value) || value <= 0) {\n return '0 B';\n }\n\n const units = ['B', 'KB', 'MB', 'GB'] as const;\n let size = value;\n let unitIndex = 0;\n\n while (size >= 1024 && unitIndex < units.length - 1) {\n size /= 1024;\n unitIndex += 1;\n }\n\n const unit = units.at(unitIndex) ?? 'B';\n const rounded = unitIndex === 0 ? Math.round(size) : Math.round(size * 10) / 10;\n return `${rounded} ${unit}`;\n}\n"]}
@@ -0,0 +1,5 @@
1
+ import type { UploadAsset } from '../../../../types/upload';
2
+ import type { UploadPickerInput } from '../../application/ports/outbound/UploadPickerPort';
3
+ /*** Picks one document through Expo DocumentPicker and maps it to the ZORA upload contract. */
4
+ export declare function pickDocumentOrFileAsync(input: UploadPickerInput): Promise<UploadAsset | null>;
5
+ //# sourceMappingURL=pickDocumentOrFileAsync.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pickDocumentOrFileAsync.d.ts","sourceRoot":"","sources":["../../../../../src/features/uploader/adapters/outbound/pickDocumentOrFileAsync.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mDAAmD,CAAC;AAE3F,+FAA+F;AAC/F,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE,iBAAiB,GACvB,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAuB7B"}
@@ -0,0 +1,38 @@
1
+ import * as DocumentPicker from 'expo-document-picker';
2
+ /*** Picks one document through Expo DocumentPicker and maps it to the ZORA upload contract. */
3
+ export async function pickDocumentOrFileAsync(input) {
4
+ const result = await DocumentPicker.getDocumentAsync({
5
+ copyToCacheDirectory: true,
6
+ multiple: false,
7
+ type: resolveDocumentMimeTypes(input.accept),
8
+ });
9
+ if (result.canceled) {
10
+ return null;
11
+ }
12
+ const asset = result.assets.at(0);
13
+ if (!asset) {
14
+ return null;
15
+ }
16
+ return {
17
+ kind: 'local',
18
+ uri: asset.uri,
19
+ fileName: asset.name,
20
+ sizeBytes: asset.size,
21
+ contentType: asset.mimeType,
22
+ };
23
+ }
24
+ /*** Maps browser-style accept MIME entries into Expo DocumentPicker's type option. */
25
+ function resolveDocumentMimeTypes(accept) {
26
+ if (!accept) {
27
+ return '*/*';
28
+ }
29
+ const mimeTypes = accept
30
+ .split(',')
31
+ .map((token) => token.trim())
32
+ .filter((token) => token.includes('/'));
33
+ if (mimeTypes.length === 0) {
34
+ return '*/*';
35
+ }
36
+ return mimeTypes.length === 1 ? (mimeTypes.at(0) ?? '*/*') : mimeTypes;
37
+ }
38
+ //# sourceMappingURL=pickDocumentOrFileAsync.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pickDocumentOrFileAsync.js","sourceRoot":"","sources":["../../../../../src/features/uploader/adapters/outbound/pickDocumentOrFileAsync.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,cAAc,MAAM,sBAAsB,CAAC;AAKvD,+FAA+F;AAC/F,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,KAAwB;IAExB,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,gBAAgB,CAAC;QACnD,oBAAoB,EAAE,IAAI;QAC1B,QAAQ,EAAE,KAAK;QACf,IAAI,EAAE,wBAAwB,CAAC,KAAK,CAAC,MAAM,CAAC;KAC7C,CAAC,CAAC;IAEH,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;QACpB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAClC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO;QACL,IAAI,EAAE,OAAO;QACb,GAAG,EAAE,KAAK,CAAC,GAAG;QACd,QAAQ,EAAE,KAAK,CAAC,IAAI;QACpB,SAAS,EAAE,KAAK,CAAC,IAAI;QACrB,WAAW,EAAE,KAAK,CAAC,QAAQ;KAC5B,CAAC;AACJ,CAAC;AAED,sFAAsF;AACtF,SAAS,wBAAwB,CAAC,MAA0B;IAC1D,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,SAAS,GAAG,MAAM;SACrB,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;SAC5B,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;IAE1C,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3B,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,SAAS,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AACzE,CAAC","sourcesContent":["import * as DocumentPicker from 'expo-document-picker';\n\nimport type { UploadAsset } from '../../../../types/upload';\nimport type { UploadPickerInput } from '../../application/ports/outbound/UploadPickerPort';\n\n/*** Picks one document through Expo DocumentPicker and maps it to the ZORA upload contract. */\nexport async function pickDocumentOrFileAsync(\n input: UploadPickerInput,\n): Promise<UploadAsset | null> {\n const result = await DocumentPicker.getDocumentAsync({\n copyToCacheDirectory: true,\n multiple: false,\n type: resolveDocumentMimeTypes(input.accept),\n });\n\n if (result.canceled) {\n return null;\n }\n\n const asset = result.assets.at(0);\n if (!asset) {\n return null;\n }\n\n return {\n kind: 'local',\n uri: asset.uri,\n fileName: asset.name,\n sizeBytes: asset.size,\n contentType: asset.mimeType,\n };\n}\n\n/*** Maps browser-style accept MIME entries into Expo DocumentPicker's type option. */\nfunction resolveDocumentMimeTypes(accept: string | undefined): string | string[] {\n if (!accept) {\n return '*/*';\n }\n\n const mimeTypes = accept\n .split(',')\n .map((token) => token.trim())\n .filter((token) => token.includes('/'));\n\n if (mimeTypes.length === 0) {\n return '*/*';\n }\n\n return mimeTypes.length === 1 ? (mimeTypes.at(0) ?? '*/*') : mimeTypes;\n}\n"]}
@@ -0,0 +1,5 @@
1
+ import type { UploadAsset } from '../../../../types/upload';
2
+ import type { UploadPickerInput } from '../../application/ports/outbound/UploadPickerPort';
3
+ /*** Picks one image or video through Expo ImagePicker and maps it to the ZORA upload contract. */
4
+ export declare function pickImageOrVideoAsync(input: UploadPickerInput): Promise<UploadAsset | null>;
5
+ //# sourceMappingURL=pickImageOrVideoAsync.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pickImageOrVideoAsync.d.ts","sourceRoot":"","sources":["../../../../../src/features/uploader/adapters/outbound/pickImageOrVideoAsync.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mDAAmD,CAAC;AAE3F,kGAAkG;AAClG,wBAAsB,qBAAqB,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAuBjG"}
@@ -0,0 +1,24 @@
1
+ import * as ImagePicker from 'expo-image-picker';
2
+ /*** Picks one image or video through Expo ImagePicker and maps it to the ZORA upload contract. */
3
+ export async function pickImageOrVideoAsync(input) {
4
+ const result = await ImagePicker.launchImageLibraryAsync({
5
+ mediaTypes: input.type === 'video' ? ['videos'] : ['images'],
6
+ });
7
+ if (result.canceled) {
8
+ return null;
9
+ }
10
+ const asset = result.assets.at(0);
11
+ if (!asset) {
12
+ return null;
13
+ }
14
+ return {
15
+ kind: 'local',
16
+ uri: asset.uri,
17
+ fileName: asset.fileName ?? undefined,
18
+ sizeBytes: asset.fileSize,
19
+ contentType: asset.mimeType ?? undefined,
20
+ width: asset.width > 0 ? asset.width : undefined,
21
+ height: asset.height > 0 ? asset.height : undefined,
22
+ };
23
+ }
24
+ //# sourceMappingURL=pickImageOrVideoAsync.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pickImageOrVideoAsync.js","sourceRoot":"","sources":["../../../../../src/features/uploader/adapters/outbound/pickImageOrVideoAsync.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,WAAW,MAAM,mBAAmB,CAAC;AAKjD,kGAAkG;AAClG,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,KAAwB;IAClE,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,uBAAuB,CAAC;QACvD,UAAU,EAAE,KAAK,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;KAC7D,CAAC,CAAC;IAEH,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;QACpB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAClC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO;QACL,IAAI,EAAE,OAAO;QACb,GAAG,EAAE,KAAK,CAAC,GAAG;QACd,QAAQ,EAAE,KAAK,CAAC,QAAQ,IAAI,SAAS;QACrC,SAAS,EAAE,KAAK,CAAC,QAAQ;QACzB,WAAW,EAAE,KAAK,CAAC,QAAQ,IAAI,SAAS;QACxC,KAAK,EAAE,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS;QAChD,MAAM,EAAE,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS;KACpD,CAAC;AACJ,CAAC","sourcesContent":["import * as ImagePicker from 'expo-image-picker';\n\nimport type { UploadAsset } from '../../../../types/upload';\nimport type { UploadPickerInput } from '../../application/ports/outbound/UploadPickerPort';\n\n/*** Picks one image or video through Expo ImagePicker and maps it to the ZORA upload contract. */\nexport async function pickImageOrVideoAsync(input: UploadPickerInput): Promise<UploadAsset | null> {\n const result = await ImagePicker.launchImageLibraryAsync({\n mediaTypes: input.type === 'video' ? ['videos'] : ['images'],\n });\n\n if (result.canceled) {\n return null;\n }\n\n const asset = result.assets.at(0);\n if (!asset) {\n return null;\n }\n\n return {\n kind: 'local',\n uri: asset.uri,\n fileName: asset.fileName ?? undefined,\n sizeBytes: asset.fileSize,\n contentType: asset.mimeType ?? undefined,\n width: asset.width > 0 ? asset.width : undefined,\n height: asset.height > 0 ? asset.height : undefined,\n };\n}\n"]}