@xsolla/xui-image-uploader 0.158.0 → 0.159.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/README.md +40 -36
- package/native/index.d.mts +2 -0
- package/native/index.d.ts +2 -0
- package/native/index.js +5 -2
- package/native/index.js.map +1 -1
- package/native/index.mjs +5 -2
- package/native/index.mjs.map +1 -1
- package/package.json +5 -5
- package/web/index.d.mts +2 -0
- package/web/index.d.ts +2 -0
- package/web/index.js +8 -3
- package/web/index.js.map +1 -1
- package/web/index.mjs +8 -3
- package/web/index.mjs.map +1 -1
package/README.md
CHANGED
|
@@ -13,12 +13,15 @@ npm install @xsolla/xui-image-uploader
|
|
|
13
13
|
### Basic (Uncontrolled)
|
|
14
14
|
|
|
15
15
|
```tsx
|
|
16
|
-
import * as React from
|
|
17
|
-
import {
|
|
16
|
+
import * as React from "react";
|
|
17
|
+
import {
|
|
18
|
+
ImageUploader,
|
|
19
|
+
type ImageUploaderFile,
|
|
20
|
+
} from "@xsolla/xui-image-uploader";
|
|
18
21
|
|
|
19
22
|
export default function Basic() {
|
|
20
23
|
const handleUpload = (file: ImageUploaderFile) => {
|
|
21
|
-
console.log(
|
|
24
|
+
console.log("Picked file:", file.name, file.size);
|
|
22
25
|
};
|
|
23
26
|
|
|
24
27
|
return <ImageUploader onUpload={handleUpload} />;
|
|
@@ -28,11 +31,11 @@ export default function Basic() {
|
|
|
28
31
|
### Controlled
|
|
29
32
|
|
|
30
33
|
```tsx
|
|
31
|
-
import * as React from
|
|
34
|
+
import * as React from "react";
|
|
32
35
|
import {
|
|
33
36
|
ImageUploader,
|
|
34
37
|
type ImageUploaderValue,
|
|
35
|
-
} from
|
|
38
|
+
} from "@xsolla/xui-image-uploader";
|
|
36
39
|
|
|
37
40
|
export default function Controlled() {
|
|
38
41
|
const [value, setValue] = React.useState<ImageUploaderValue | null>(null);
|
|
@@ -50,8 +53,8 @@ export default function Controlled() {
|
|
|
50
53
|
### Wide View with Description
|
|
51
54
|
|
|
52
55
|
```tsx
|
|
53
|
-
import * as React from
|
|
54
|
-
import { ImageUploader } from
|
|
56
|
+
import * as React from "react";
|
|
57
|
+
import { ImageUploader } from "@xsolla/xui-image-uploader";
|
|
55
58
|
|
|
56
59
|
export default function WideView() {
|
|
57
60
|
return (
|
|
@@ -77,15 +80,15 @@ If `onUpload` returns a `Promise`, the component automatically:
|
|
|
77
80
|
You don't need `async`/`await` in the handler — just return the upload promise:
|
|
78
81
|
|
|
79
82
|
```tsx
|
|
80
|
-
import { ImageUploader } from
|
|
83
|
+
import { ImageUploader } from "@xsolla/xui-image-uploader";
|
|
81
84
|
|
|
82
85
|
export default function AutoUpload() {
|
|
83
86
|
return (
|
|
84
87
|
<ImageUploader
|
|
85
88
|
uploadingPlaceholder="Uploading…"
|
|
86
89
|
// uploadFn can return Promise<string> or Promise<{ url, filename? }>
|
|
87
|
-
onUpload={file => uploadFn(file.file)}
|
|
88
|
-
onChange={value => form.setFieldValue(
|
|
90
|
+
onUpload={(file) => uploadFn(file.file)}
|
|
91
|
+
onChange={(value) => form.setFieldValue("avatar", value?.url ?? null)}
|
|
89
92
|
/>
|
|
90
93
|
);
|
|
91
94
|
}
|
|
@@ -97,11 +100,11 @@ For finer-grained control (e.g. uploads triggered outside the component, or
|
|
|
97
100
|
to keep the spinner visible during post-upload work), pass `loading` explicitly:
|
|
98
101
|
|
|
99
102
|
```tsx
|
|
100
|
-
import * as React from
|
|
103
|
+
import * as React from "react";
|
|
101
104
|
import {
|
|
102
105
|
ImageUploader,
|
|
103
106
|
type ImageUploaderFile,
|
|
104
|
-
} from
|
|
107
|
+
} from "@xsolla/xui-image-uploader";
|
|
105
108
|
|
|
106
109
|
export default function WithLoading() {
|
|
107
110
|
const [loading, setLoading] = React.useState(false);
|
|
@@ -128,8 +131,8 @@ export default function WithLoading() {
|
|
|
128
131
|
### Error State
|
|
129
132
|
|
|
130
133
|
```tsx
|
|
131
|
-
import * as React from
|
|
132
|
-
import { ImageUploader } from
|
|
134
|
+
import * as React from "react";
|
|
135
|
+
import { ImageUploader } from "@xsolla/xui-image-uploader";
|
|
133
136
|
|
|
134
137
|
export default function WithError() {
|
|
135
138
|
return (
|
|
@@ -148,12 +151,12 @@ returns a normalized `ImageUploaderFile` (e.g. wrapping `expo-image-picker` or
|
|
|
148
151
|
`react-native-image-picker`).
|
|
149
152
|
|
|
150
153
|
```tsx
|
|
151
|
-
import * as React from
|
|
152
|
-
import * as ImagePicker from
|
|
154
|
+
import * as React from "react";
|
|
155
|
+
import * as ImagePicker from "expo-image-picker";
|
|
153
156
|
import {
|
|
154
157
|
ImageUploader,
|
|
155
158
|
type ImageUploaderFile,
|
|
156
|
-
} from
|
|
159
|
+
} from "@xsolla/xui-image-uploader";
|
|
157
160
|
|
|
158
161
|
export default function Native() {
|
|
159
162
|
const openPicker = async (): Promise<ImageUploaderFile | null> => {
|
|
@@ -163,7 +166,7 @@ export default function Native() {
|
|
|
163
166
|
if (result.canceled) return null;
|
|
164
167
|
const asset = result.assets[0];
|
|
165
168
|
return {
|
|
166
|
-
name: asset.fileName ??
|
|
169
|
+
name: asset.fileName ?? "image",
|
|
167
170
|
size: asset.fileSize ?? 0,
|
|
168
171
|
uri: asset.uri,
|
|
169
172
|
mimeType: asset.mimeType,
|
|
@@ -197,24 +200,25 @@ always discoverable.
|
|
|
197
200
|
|
|
198
201
|
### ImageUploader
|
|
199
202
|
|
|
200
|
-
| Prop
|
|
201
|
-
|
|
|
202
|
-
|
|
|
203
|
-
|
|
|
204
|
-
|
|
|
205
|
-
|
|
|
206
|
-
|
|
|
207
|
-
|
|
|
208
|
-
|
|
|
209
|
-
|
|
|
210
|
-
|
|
|
211
|
-
|
|
|
212
|
-
|
|
|
213
|
-
|
|
|
214
|
-
|
|
|
215
|
-
|
|
|
216
|
-
|
|
|
217
|
-
|
|
|
203
|
+
| Prop | Type | Default | Description |
|
|
204
|
+
| :------------------- | :----------------------------------------------------------------------------------- | :------------ | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
205
|
+
| `testID` | `string` | — | Test ID for testing frameworks. On web this renders as `data-testid`; on React Native it renders as `testID`. |
|
|
206
|
+
| size | `"xl" \| "lg" \| "md" \| "sm" \| "xs"` | `"xl"` | Visual size of the uploader. |
|
|
207
|
+
| placeholder | `React.ReactNode` | `"Upload"` | Label shown under the icon (or filename in error state). Accepts a string or a custom React node. |
|
|
208
|
+
| uploadingPlaceholder | `React.ReactNode` | `"Uploading"` | Label shown while `loading` is true. Accepts a string or a custom React node. |
|
|
209
|
+
| description | `React.ReactNode` | - | Helper text below the placeholder. Only rendered when `wideView` is true. |
|
|
210
|
+
| errorMessage | `string` | - | Error message — when provided, the component renders in the error state. |
|
|
211
|
+
| wideView | `boolean` | `false` | Use the horizontal layout. The box stretches to fill its parent's width. |
|
|
212
|
+
| disabled | `boolean` | `false` | Disabled state. |
|
|
213
|
+
| loading | `boolean` | `false` | Controlled loading state (shows spinner + uploading label). |
|
|
214
|
+
| value | `ImageUploaderValue \| null` | - | Controlled value. When provided, the component reflects this value. |
|
|
215
|
+
| onUpload | `(file: ImageUploaderFile) => void \| Promise<ImageUploaderValue \| string \| void>` | - | Fires when the user picks (or drops) a file. If it returns a `Promise`, the component shows the uploading state until it settles, forwards a resolved value (object or bare URL string) to `onChange`, and forwards a rejection to `onChange(null, error)`. |
|
|
216
|
+
| onChange | `(value: ImageUploaderValue \| null, error?: Error) => void` | - | Fires when the displayed value changes — on file pick (with a local data-URL preview), after `onUpload` resolves (with the server value), or on remove (`null`). The optional `error` arg carries any rejection from `onUpload`. |
|
|
217
|
+
| onDelete | `() => void` | - | Fires when the user removes the image (trash click / clear). |
|
|
218
|
+
| accept | `string` | `"image/*"` | Accepted MIME types for the file input (web only). |
|
|
219
|
+
| openPicker | `() => Promise<ImageUploaderFile \| null>` | - | Native file picker hook. Required on native (no DOM `<input type="file">`). On web, omit this and the component falls back to a hidden file input. |
|
|
220
|
+
| themeMode | `ThemeMode` | - | Override the theme mode for this instance. |
|
|
221
|
+
| themeProductContext | `ThemeProductContext` | - | Override the product theme context for this instance. |
|
|
218
222
|
|
|
219
223
|
### ImageUploaderValue
|
|
220
224
|
|
package/native/index.d.mts
CHANGED
|
@@ -79,6 +79,8 @@ interface ImageUploaderProps extends ThemeOverrideProps {
|
|
|
79
79
|
* On web, omit this and the component falls back to a hidden file input.
|
|
80
80
|
*/
|
|
81
81
|
openPicker?: () => Promise<ImageUploaderFile | null>;
|
|
82
|
+
/** Test ID for testing frameworks */
|
|
83
|
+
testID?: string;
|
|
82
84
|
}
|
|
83
85
|
declare const ImageUploader: React.ForwardRefExoticComponent<ImageUploaderProps & React.RefAttributes<HTMLInputElement>>;
|
|
84
86
|
|
package/native/index.d.ts
CHANGED
|
@@ -79,6 +79,8 @@ interface ImageUploaderProps extends ThemeOverrideProps {
|
|
|
79
79
|
* On web, omit this and the component falls back to a hidden file input.
|
|
80
80
|
*/
|
|
81
81
|
openPicker?: () => Promise<ImageUploaderFile | null>;
|
|
82
|
+
/** Test ID for testing frameworks */
|
|
83
|
+
testID?: string;
|
|
82
84
|
}
|
|
83
85
|
declare const ImageUploader: React.ForwardRefExoticComponent<ImageUploaderProps & React.RefAttributes<HTMLInputElement>>;
|
|
84
86
|
|
package/native/index.js
CHANGED
|
@@ -238,6 +238,8 @@ var Text = ({
|
|
|
238
238
|
numberOfLines,
|
|
239
239
|
id,
|
|
240
240
|
role,
|
|
241
|
+
testID,
|
|
242
|
+
"data-testid": dataTestId,
|
|
241
243
|
style: styleProp,
|
|
242
244
|
...props
|
|
243
245
|
}) => {
|
|
@@ -267,7 +269,7 @@ var Text = ({
|
|
|
267
269
|
{
|
|
268
270
|
style: baseStyle,
|
|
269
271
|
numberOfLines,
|
|
270
|
-
testID: id,
|
|
272
|
+
testID: dataTestId || testID || id,
|
|
271
273
|
accessibilityRole,
|
|
272
274
|
children
|
|
273
275
|
}
|
|
@@ -298,6 +300,7 @@ var ImageUploader = (0, import_react.forwardRef)(
|
|
|
298
300
|
onDelete,
|
|
299
301
|
disabled = false,
|
|
300
302
|
loading = false,
|
|
303
|
+
testID,
|
|
301
304
|
themeMode,
|
|
302
305
|
themeProductContext
|
|
303
306
|
}, ref) => {
|
|
@@ -612,7 +615,7 @@ var ImageUploader = (0, import_react.forwardRef)(
|
|
|
612
615
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
613
616
|
Box,
|
|
614
617
|
{
|
|
615
|
-
|
|
618
|
+
testID: testID || "image-uploader",
|
|
616
619
|
flexDirection: "column",
|
|
617
620
|
gap: rootGap,
|
|
618
621
|
alignItems: "flex-start",
|
package/native/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/index.tsx","../../src/ImageUploader.tsx","../../../../foundation/primitives-native/src/Box.tsx","../../../../foundation/primitives-native/src/Text.tsx","../../../../foundation/primitives-native/src/index.tsx"],"sourcesContent":["export * from \"./ImageUploader\";\n","import React, { useRef, useState, useEffect, forwardRef } from \"react\";\n// @ts-expect-error - this will be resolved at build time\nimport { Box, Text, isWeb } from \"@xsolla/xui-primitives\";\nimport {\n useId,\n useResolvedTheme,\n type ThemeOverrideProps,\n} from \"@xsolla/xui-core\";\nimport { Image, TrashCan } from \"@xsolla/xui-icons-base\";\nimport { Spinner } from \"@xsolla/xui-spinner\";\n\nexport type ImageUploaderSize = \"xl\" | \"lg\" | \"md\" | \"sm\" | \"xs\";\n\n/** Controlled value shape. */\nexport interface ImageUploaderValue {\n filename?: string;\n url?: string;\n}\n\n/**\n * Normalized file shape produced by the platform picker / drag-drop pipeline.\n * - `uri` is a data-URL on web and a file URI on native.\n * - On web, the original `File` is passed through for consumers that need it\n * (e.g. to upload via FormData / fetch).\n */\nexport type ImageUploaderFile = {\n name: string;\n size: number;\n uri: string;\n mimeType?: string;\n /** The original DOM File (web only) */\n file?: File;\n};\n\nexport interface ImageUploaderProps extends ThemeOverrideProps {\n /** Size of the uploader. Figma default is `xl`. */\n size?: ImageUploaderSize;\n /** Placeholder shown under the icon. Accepts a string or a custom React node. */\n placeholder?: React.ReactNode;\n /** Placeholder shown while the file is uploading. Accepts a string or a custom React node. */\n uploadingPlaceholder?: React.ReactNode;\n /** Description below the placeholder. Only rendered when `wideView` is true. Accepts a string or a custom React node. */\n description?: React.ReactNode;\n /** Error message — when provided, component renders in the error state. */\n errorMessage?: string;\n /** Wide view (horizontal layout). When true, the box stretches to its parent's full width. */\n wideView?: boolean;\n /** Disabled state. */\n disabled?: boolean;\n /** Controlled loading state (shows spinner + \"Uploading\" label). */\n loading?: boolean;\n\n /** Controlled value. When provided, the component reflects this value. */\n value?: ImageUploaderValue | null;\n\n /**\n * Fires when the user picks (or drops) a file. Use this to perform the\n * actual upload — the component itself does no I/O.\n *\n * If the handler returns a `Promise`:\n * - The component automatically shows the uploading state (spinner +\n * `uploadingPlaceholder`) until the promise settles.\n * - If the promise resolves to an `ImageUploaderValue` (`{url, filename?}`)\n * or a `string` URL, the component automatically calls `onChange(value)`\n * with that value. The string form is sugar for `{url: <string>}`.\n * - If the promise rejects, the component calls `onChange(null, error)`.\n *\n * For finer-grained control, pass `loading` explicitly instead.\n */\n onUpload?: (\n file: ImageUploaderFile\n ) => void | Promise<ImageUploaderValue | string | void>;\n /**\n * Fires when the displayed value changes — on file pick (with a local\n * data-URL preview), after upload resolves (with the server value), or on\n * remove (`null`). The optional second argument carries any error thrown by\n * the consumer's `onUpload`.\n */\n onChange?: (value: ImageUploaderValue | null, error?: Error) => void;\n /** Fires when the user removes the image (trash click / clear). */\n onDelete?: () => void;\n\n /**\n * Accepted file types — passed through to the hidden `<input accept>` as a\n * picker hint (web only — ignored on native). Standard HTML syntax:\n * comma-separated list of MIME types (`image/png`), MIME wildcards\n * (`image/*`), or file extensions (`.png`). Defaults to `image/*`.\n *\n * No runtime validation is performed — the consumer's `onUpload` (or the\n * backend it calls) is responsible for accepting/rejecting files.\n */\n accept?: string;\n /**\n * Native file picker hook. Required on native (no DOM `<input type=\"file\">`).\n * On web, omit this and the component falls back to a hidden file input.\n */\n openPicker?: () => Promise<ImageUploaderFile | null>;\n}\n\ntype InternalState =\n | \"default\"\n | \"hover\"\n | \"focus\"\n | \"uploading\"\n | \"uploaded\"\n | \"uploadedHover\"\n | \"error\"\n | \"disable\";\n\nexport const ImageUploader = forwardRef<HTMLInputElement, ImageUploaderProps>(\n (\n {\n size = \"xl\",\n placeholder = \"Upload\",\n uploadingPlaceholder = \"Uploading\",\n description,\n errorMessage,\n wideView = false,\n accept = \"image/*\",\n openPicker,\n value,\n onUpload,\n onChange,\n onDelete,\n disabled = false,\n loading = false,\n themeMode,\n themeProductContext,\n },\n ref\n ) => {\n const { theme } = useResolvedTheme({ themeMode, themeProductContext });\n const fileInputRef = useRef<HTMLInputElement>(null);\n const isControlled = value !== undefined;\n const [internalPreview, setInternalPreview] = useState<string | null>(null);\n const [isAutoUploading, setIsAutoUploading] = useState(false);\n // Captures errors thrown by the consumer's `onUpload`. Surfaced via the\n // component's normal error UI when no explicit `errorMessage` prop is set.\n const [autoError, setAutoError] = useState<string | null>(null);\n const [isHover, setIsHover] = useState(false);\n const [isFocus, setIsFocus] = useState(false);\n const [isPreviewHover, setIsPreviewHover] = useState(false);\n // Touch / native devices have no hover, so the trash overlay would never\n // appear. Detect once on mount and force `uploadedHover` for those cases.\n const [isHoverless] = useState(\n () =>\n !isWeb ||\n (typeof window !== \"undefined\" &&\n !!window.matchMedia?.(\"(hover: none)\").matches)\n );\n\n const reactId = useId();\n const baseId = `image-uploader-${reactId.replace(/[^a-zA-Z0-9-]/g, \"\")}`;\n const descriptionId = `${baseId}-description`;\n const errorId = `${baseId}-error`;\n\n const preview = isControlled ? (value?.url ?? null) : internalPreview;\n\n useEffect(() => {\n if (!isControlled && value === null) setInternalPreview(null);\n }, [isControlled, value]);\n\n React.useImperativeHandle(\n ref,\n () => fileInputRef.current as HTMLInputElement,\n []\n );\n\n const sizeStyles = theme.sizing.imageUploader(size);\n const inputColors = theme.colors.control.input;\n const focusColors = theme.colors.control.focus;\n const alertBorder = theme.colors.control.alert.border;\n const alertText = theme.colors.content.alert.primary;\n const scrim = theme.colors.layer.scrim;\n\n // Resolved error message: explicit prop wins, fall back to caught upload error.\n const resolvedError = errorMessage || autoError;\n const hasError = !!resolvedError;\n\n let state: InternalState;\n if (disabled) state = \"disable\";\n else if (loading || isAutoUploading) state = \"uploading\";\n else if (hasError) state = \"error\";\n else if (preview)\n state = isPreviewHover || isHoverless ? \"uploadedHover\" : \"uploaded\";\n else if (isFocus) state = \"focus\";\n else if (isHover) state = \"hover\";\n else state = \"default\";\n\n const emitFile = (img: ImageUploaderFile) => {\n // Clear any prior error from a previous upload attempt.\n setAutoError(null);\n if (!isControlled) setInternalPreview(img.uri);\n onChange?.({\n filename: img.name,\n url: img.uri,\n });\n const result = onUpload?.(img);\n // If the consumer's onUpload returns a Promise:\n // - show the uploading state until it settles\n // - on resolve with a value, treat it as the canonical onChange payload\n // - on reject, surface the error internally AND via onChange(null, error)\n if (result && typeof (result as Promise<unknown>).then === \"function\") {\n setIsAutoUploading(true);\n Promise.resolve(result)\n .then((uploaded) => {\n // Accept either an ImageUploaderValue ({url, filename?}) or a\n // bare string URL as sugar for {url: <string>}.\n let next: ImageUploaderValue | null = null;\n if (typeof uploaded === \"string\") {\n next = { url: uploaded };\n } else if (\n uploaded &&\n typeof uploaded === \"object\" &&\n \"url\" in uploaded\n ) {\n next = uploaded as ImageUploaderValue;\n }\n if (next) {\n if (!isControlled) setInternalPreview(next.url ?? null);\n onChange?.(next);\n }\n })\n .catch((err: Error) => {\n // Surface internally so the error UI lights up without the\n // consumer having to wire up local state.\n setAutoError(err?.message || \"Upload failed\");\n // Reset preview so we don't keep showing the failed pick.\n if (!isControlled) setInternalPreview(null);\n onChange?.(null, err);\n })\n .finally(() => setIsAutoUploading(false));\n }\n };\n\n const handleWebFile = (file: File) => {\n const reader = new FileReader();\n reader.onload = (e) => {\n const uri = e.target?.result as string;\n // Mutate the DOM File so it satisfies BOTH the DOM File interface (for\n // direct use in FormData / fetch / .size checks) and ImageUploaderFile\n // (with .uri / .mimeType added). `.file` self-references for back-compat\n // with consumers that previously dereferenced `imageFile.file`.\n const enriched = file as File & ImageUploaderFile;\n (enriched as { uri?: string }).uri = uri;\n (enriched as { mimeType?: string }).mimeType = file.type;\n (enriched as { file?: File }).file = enriched;\n emitFile(enriched);\n };\n reader.readAsDataURL(file);\n };\n\n const handleClick = async () => {\n if (disabled || loading) return;\n if (preview) {\n removeImage();\n return;\n }\n if (openPicker) {\n const picked = await openPicker();\n if (picked) emitFile(picked);\n return;\n }\n if (isWeb) {\n fileInputRef.current?.click();\n }\n };\n\n const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const file = e.target.files?.[0];\n e.target.value = \"\";\n if (file) handleWebFile(file);\n };\n\n const handleDragOver = (e: React.DragEvent) => {\n e.preventDefault();\n e.stopPropagation();\n if (!disabled && !loading && !preview) setIsHover(true);\n };\n\n const handleDragLeave = (e: React.DragEvent) => {\n e.preventDefault();\n e.stopPropagation();\n setIsHover(false);\n };\n\n const handleDrop = (e: React.DragEvent) => {\n e.preventDefault();\n e.stopPropagation();\n setIsHover(false);\n if (disabled || loading || preview) return;\n const file = e.dataTransfer.files?.[0];\n if (file) handleWebFile(file);\n };\n\n const removeImage = (e?: { stopPropagation?: () => void }) => {\n e?.stopPropagation?.();\n setAutoError(null);\n if (!isControlled) setInternalPreview(null);\n setIsPreviewHover(false);\n onDelete?.();\n onChange?.(null);\n if (isWeb && fileInputRef.current) fileInputRef.current.value = \"\";\n };\n\n let backgroundColor: string = inputColors.bg;\n let borderColor: string = inputColors.border;\n let borderStyle: \"dashed\" | \"solid\" = \"dashed\";\n let labelColor: string = inputColors.text;\n let descriptionColor: string = inputColors.placeholder;\n let iconColor: string = inputColors.text;\n\n switch (state) {\n case \"hover\":\n backgroundColor = inputColors.bgHover;\n borderColor = inputColors.borderHover;\n break;\n case \"focus\":\n case \"uploading\":\n backgroundColor = focusColors.bg;\n borderColor = focusColors.border;\n break;\n case \"uploaded\":\n case \"uploadedHover\":\n backgroundColor = \"transparent\";\n borderColor = \"transparent\";\n break;\n case \"error\":\n backgroundColor = inputColors.bg;\n borderColor = alertBorder;\n borderStyle = \"solid\";\n labelColor = alertText;\n iconColor = alertText;\n break;\n case \"disable\":\n backgroundColor = inputColors.bgDisable;\n borderColor = inputColors.borderDisable;\n labelColor = inputColors.textDisable;\n descriptionColor = inputColors.textDisable;\n iconColor = inputColors.textDisable;\n break;\n }\n\n const rootGap = state === \"error\" ? sizeStyles.errorGap : 0;\n const placeholderContent =\n state === \"uploading\" ? uploadingPlaceholder : placeholder;\n\n const renderInner = () => {\n if (state === \"uploaded\" || state === \"uploadedHover\") {\n return (\n <>\n <Box\n as=\"img\"\n src={preview || undefined}\n alt={value?.filename ?? \"Uploaded image\"}\n position=\"absolute\"\n top={0}\n left={0}\n right={0}\n bottom={0}\n borderRadius={sizeStyles.radius}\n resizeMode=\"cover\"\n {...(isWeb && { width: \"100%\", height: \"100%\" })}\n style={isWeb ? { objectFit: \"cover\" } : undefined}\n />\n {state === \"uploadedHover\" && !disabled && (\n <>\n <Box\n position=\"absolute\"\n top={0}\n left={0}\n right={0}\n bottom={0}\n backgroundColor={scrim}\n borderRadius={sizeStyles.radius}\n {...(isWeb && { \"aria-hidden\": \"true\" })}\n />\n <Box\n position=\"relative\"\n width={sizeStyles.iconSize}\n height={sizeStyles.iconSize}\n alignItems=\"center\"\n justifyContent=\"center\"\n zIndex={1}\n pointerEvents=\"none\"\n {...(isWeb && { \"aria-hidden\": \"true\" })}\n >\n <TrashCan size={sizeStyles.iconSize} color=\"#ffffff\" />\n </Box>\n </>\n )}\n </>\n );\n }\n\n return (\n <>\n {state === \"uploading\" ? (\n <Spinner\n size={size}\n color={theme.colors.content.brand.primary}\n {...(isWeb && { \"aria-hidden\": \"true\" })}\n />\n ) : (\n <Box {...(isWeb && { \"aria-hidden\": \"true\" })}>\n <Image size={sizeStyles.iconSize} color={iconColor} />\n </Box>\n )}\n\n {placeholderContent &&\n (typeof placeholderContent === \"string\" ? (\n <Text\n data-testid=\"image-uploader__placeholder\"\n color={labelColor}\n fontSize={sizeStyles.labelFontSize}\n lineHeight={sizeStyles.labelLineHeight}\n fontWeight=\"500\"\n textAlign=\"center\"\n numberOfLines={1}\n style={\n isWeb\n ? {\n maxWidth: \"100%\",\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n whiteSpace: \"nowrap\",\n }\n : undefined\n }\n >\n {placeholderContent}\n </Text>\n ) : (\n <Box data-testid=\"image-uploader__placeholder\">\n {placeholderContent}\n </Box>\n ))}\n\n {description &&\n wideView &&\n state !== \"uploading\" &&\n state !== \"error\" &&\n (typeof description === \"string\" ? (\n <Text\n data-testid=\"image-uploader__description\"\n color={descriptionColor}\n fontSize={sizeStyles.descriptionFontSize}\n lineHeight={sizeStyles.descriptionLineHeight}\n textAlign=\"center\"\n numberOfLines={1}\n style={\n isWeb\n ? {\n maxWidth: \"100%\",\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n whiteSpace: \"nowrap\",\n }\n : undefined\n }\n {...(isWeb && { id: descriptionId })}\n >\n {description}\n </Text>\n ) : (\n <Box\n data-testid=\"image-uploader__description\"\n {...(isWeb && { id: descriptionId })}\n >\n {description}\n </Box>\n ))}\n </>\n );\n };\n\n // Web-only DOM event handlers — stripped on native.\n const webOnlyHandlers = isWeb\n ? {\n onMouseEnter: () => {\n if (disabled || loading) return;\n if (preview) setIsPreviewHover(true);\n else setIsHover(true);\n },\n onMouseLeave: () => {\n if (preview) setIsPreviewHover(false);\n else setIsHover(false);\n },\n onFocus: (e: React.FocusEvent<HTMLElement>) => {\n if (disabled || loading || preview) return;\n // Only show the focus visual state for keyboard focus, not for\n // pointer focus (e.g. after the native file picker is cancelled).\n if (\n typeof e.target?.matches === \"function\" &&\n !e.target.matches(\":focus-visible\")\n ) {\n return;\n }\n setIsFocus(true);\n },\n onBlur: () => setIsFocus(false),\n onDragOver: handleDragOver,\n onDragLeave: handleDragLeave,\n onDrop: handleDrop,\n }\n : {};\n\n // Web-only CSS props — dropped on native to avoid style warnings.\n const webOnlyStyle = isWeb\n ? {\n boxSizing: \"border-box\" as const,\n display: \"flex\" as const,\n cursor: (disabled || loading\n ? \"not-allowed\"\n : \"pointer\") as React.CSSProperties[\"cursor\"],\n outline: \"none\",\n transition: \"background-color 0.15s ease, border-color 0.15s ease\",\n }\n : {};\n\n // aria-label needs a string; fall back to defaults when placeholder/uploadingPlaceholder\n // are passed as React nodes.\n const buttonAriaLabel = preview\n ? `Remove image${value?.filename ? `: ${value.filename}` : \"\"}`\n : state === \"uploading\"\n ? typeof uploadingPlaceholder === \"string\"\n ? uploadingPlaceholder\n : \"Uploading\"\n : typeof placeholder === \"string\"\n ? placeholder\n : \"Upload\";\n\n const describedBy =\n [\n state === \"error\" && resolvedError ? errorId : null,\n description && wideView && state !== \"uploading\" && state !== \"error\"\n ? descriptionId\n : null,\n ]\n .filter(Boolean)\n .join(\" \") || undefined;\n\n return (\n <Box\n data-testid=\"image-uploader\"\n flexDirection=\"column\"\n gap={rootGap}\n alignItems=\"flex-start\"\n width={wideView ? \"100%\" : undefined}\n >\n {isWeb && (\n <input\n type=\"file\"\n ref={fileInputRef}\n accept={accept}\n onChange={handleFileChange}\n style={{ display: \"none\" }}\n disabled={disabled}\n tabIndex={-1}\n aria-hidden=\"true\"\n data-testid=\"image-uploader__input\"\n />\n )}\n\n <Box\n as={isWeb ? \"button\" : \"div\"}\n disabled={disabled || loading}\n data-testid=\"image-uploader__button\"\n onPress={handleClick}\n {...(isWeb && {\n \"aria-label\": buttonAriaLabel,\n \"aria-busy\": loading || undefined,\n \"aria-invalid\": state === \"error\" || undefined,\n \"aria-describedby\": describedBy,\n })}\n {...webOnlyHandlers}\n position=\"relative\"\n width={wideView ? \"100%\" : sizeStyles.box}\n height={sizeStyles.box}\n flexDirection=\"column\"\n alignItems=\"center\"\n justifyContent=\"center\"\n gap={sizeStyles.gap}\n padding={state === \"uploaded\" ? 0 : sizeStyles.padding}\n borderWidth={sizeStyles.borderWidth}\n borderStyle={borderStyle}\n borderColor={borderColor}\n borderRadius={sizeStyles.radius}\n backgroundColor={backgroundColor}\n overflow=\"hidden\"\n style={webOnlyStyle}\n >\n {renderInner()}\n </Box>\n\n {state === \"error\" && resolvedError && (\n <Text\n data-testid=\"image-uploader__error\"\n color={alertText}\n fontSize={sizeStyles.errorFontSize}\n lineHeight={sizeStyles.errorLineHeight}\n {...(isWeb && { id: errorId, role: \"alert\" })}\n >\n {resolvedError}\n </Text>\n )}\n </Box>\n );\n }\n);\n\nImageUploader.displayName = \"ImageUploader\";\n","import React from \"react\";\nimport {\n View,\n Pressable,\n Image,\n ViewStyle,\n ImageStyle,\n DimensionValue,\n AnimatableNumericValue,\n} from \"react-native\";\nimport { BoxProps } from \"@xsolla/xui-primitives-core\";\n\nexport const Box: React.FC<BoxProps> = ({\n children,\n onPress,\n onLayout,\n onMoveShouldSetResponder,\n onResponderGrant,\n onResponderMove,\n onResponderRelease,\n onResponderTerminate,\n backgroundColor,\n borderColor,\n borderWidth,\n borderBottomWidth,\n borderBottomColor,\n borderTopWidth,\n borderTopColor,\n borderLeftWidth,\n borderLeftColor,\n borderRightWidth,\n borderRightColor,\n borderRadius,\n borderStyle,\n height,\n padding,\n paddingHorizontal,\n paddingVertical,\n margin,\n marginTop,\n marginBottom,\n marginLeft,\n marginRight,\n flexDirection,\n alignItems,\n justifyContent,\n position,\n top,\n bottom,\n left,\n right,\n width,\n minWidth,\n minHeight,\n maxWidth,\n maxHeight,\n flex,\n overflow,\n zIndex,\n hoverStyle,\n pressStyle,\n style,\n \"data-testid\": dataTestId,\n testID,\n as,\n src,\n alt,\n ...rest\n}) => {\n const getContainerStyle = (pressed?: boolean): ViewStyle => ({\n backgroundColor:\n pressed && pressStyle?.backgroundColor\n ? pressStyle.backgroundColor\n : backgroundColor,\n borderColor,\n borderWidth,\n borderBottomWidth,\n borderBottomColor,\n borderTopWidth,\n borderTopColor,\n borderLeftWidth,\n borderLeftColor,\n borderRightWidth,\n borderRightColor,\n borderRadius: borderRadius as AnimatableNumericValue,\n borderStyle: borderStyle as ViewStyle[\"borderStyle\"],\n overflow,\n zIndex,\n height: height as DimensionValue,\n width: width as DimensionValue,\n minWidth: minWidth as DimensionValue,\n minHeight: minHeight as DimensionValue,\n maxWidth: maxWidth as DimensionValue,\n maxHeight: maxHeight as DimensionValue,\n padding: padding as DimensionValue,\n paddingHorizontal: paddingHorizontal as DimensionValue,\n paddingVertical: paddingVertical as DimensionValue,\n margin: margin as DimensionValue,\n marginTop: marginTop as DimensionValue,\n marginBottom: marginBottom as DimensionValue,\n marginLeft: marginLeft as DimensionValue,\n marginRight: marginRight as DimensionValue,\n flexDirection,\n alignItems,\n justifyContent,\n position: position as ViewStyle[\"position\"],\n top: top as DimensionValue,\n bottom: bottom as DimensionValue,\n left: left as DimensionValue,\n right: right as DimensionValue,\n flex,\n ...(style as ViewStyle),\n });\n\n const finalTestID = dataTestId || testID;\n\n // Destructure and drop web-only props from rest before passing to RN components\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const {\n role,\n tabIndex,\n onKeyDown,\n onKeyUp,\n \"aria-label\": _ariaLabel,\n \"aria-labelledby\": _ariaLabelledBy,\n \"aria-current\": _ariaCurrent,\n \"aria-disabled\": _ariaDisabled,\n \"aria-live\": _ariaLive,\n className,\n \"data-testid\": _dataTestId,\n ...nativeRest\n } = rest as Record<string, unknown>;\n\n // Handle as=\"img\" for React Native\n if (as === \"img\" && src) {\n const imageStyle: ImageStyle = {\n width: width as DimensionValue,\n height: height as DimensionValue,\n borderRadius: borderRadius as number,\n position: position as ImageStyle[\"position\"],\n top: top as DimensionValue,\n bottom: bottom as DimensionValue,\n left: left as DimensionValue,\n right: right as DimensionValue,\n ...(style as ImageStyle),\n };\n\n return (\n <Image\n source={{ uri: src }}\n style={imageStyle}\n testID={finalTestID}\n resizeMode=\"cover\"\n {...nativeRest}\n />\n );\n }\n\n if (onPress) {\n return (\n <Pressable\n onPress={onPress}\n onLayout={onLayout}\n onMoveShouldSetResponder={onMoveShouldSetResponder}\n onResponderGrant={onResponderGrant}\n onResponderMove={onResponderMove}\n onResponderRelease={onResponderRelease}\n onResponderTerminate={onResponderTerminate}\n style={({ pressed }) => getContainerStyle(pressed)}\n testID={finalTestID}\n {...nativeRest}\n >\n {children}\n </Pressable>\n );\n }\n\n return (\n <View\n style={getContainerStyle()}\n testID={finalTestID}\n onLayout={onLayout}\n onMoveShouldSetResponder={onMoveShouldSetResponder}\n onResponderGrant={onResponderGrant}\n onResponderMove={onResponderMove}\n onResponderRelease={onResponderRelease}\n onResponderTerminate={onResponderTerminate}\n {...nativeRest}\n >\n {children}\n </View>\n );\n};\n","import React from \"react\";\nimport {\n Text as RNText,\n TextStyle,\n AccessibilityRole,\n StyleSheet,\n} from \"react-native\";\nimport { TextProps } from \"@xsolla/xui-primitives-core\";\n\nconst roleMap: Record<string, AccessibilityRole> = {\n alert: \"alert\",\n heading: \"header\",\n button: \"button\",\n link: \"link\",\n text: \"text\",\n};\n\nconst parseNumericValue = (\n value: string | number | undefined\n): number | undefined => {\n if (value === undefined) return undefined;\n if (typeof value === \"number\") return value;\n const parsed = parseFloat(value);\n return isNaN(parsed) ? undefined : parsed;\n};\n\nexport const Text: React.FC<TextProps> = ({\n children,\n color,\n fontSize,\n fontWeight,\n fontFamily,\n textAlign,\n lineHeight,\n numberOfLines,\n id,\n role,\n style: styleProp,\n ...props\n}) => {\n let resolvedFontFamily = fontFamily\n ? fontFamily.split(\",\")[0].replace(/['\"]/g, \"\").trim()\n : undefined;\n\n if (\n resolvedFontFamily === \"Pilat Wide\" ||\n resolvedFontFamily === \"Pilat Wide Bold\" ||\n resolvedFontFamily === \"Aktiv Grotesk\"\n ) {\n resolvedFontFamily = undefined;\n }\n\n const incomingStyle = StyleSheet.flatten(styleProp) as TextStyle | undefined;\n\n const baseStyle: TextStyle = {\n color: color ?? incomingStyle?.color,\n fontSize: typeof fontSize === \"number\" ? fontSize : undefined,\n fontWeight: fontWeight as TextStyle[\"fontWeight\"],\n fontFamily: resolvedFontFamily,\n textDecorationLine: props.textDecoration as TextStyle[\"textDecorationLine\"],\n textAlign: textAlign ?? incomingStyle?.textAlign,\n lineHeight: parseNumericValue(lineHeight ?? incomingStyle?.lineHeight),\n marginTop: parseNumericValue(\n incomingStyle?.marginTop as number | string | undefined\n ),\n marginBottom: parseNumericValue(\n incomingStyle?.marginBottom as number | string | undefined\n ),\n };\n\n const accessibilityRole = role ? roleMap[role] : undefined;\n\n return (\n <RNText\n style={baseStyle}\n numberOfLines={numberOfLines}\n testID={id}\n accessibilityRole={accessibilityRole}\n >\n {children}\n </RNText>\n );\n};\n","export * from \"./Box\";\nexport * from \"./Text\";\nexport * from \"./Spinner\";\nexport * from \"./Icon\";\nexport * from \"./Divider\";\nexport * from \"./Input\";\nexport * from \"./TextArea\";\nexport * from \"./LinearGradient\";\n\nexport const isWeb = false;\nexport const isNative = true;\n"],"mappings":"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","names":["import_react_native","import_jsx_runtime","RNText","import_jsx_runtime","React"]}
|
|
1
|
+
{"version":3,"sources":["../../src/index.tsx","../../src/ImageUploader.tsx","../../../../foundation/primitives-native/src/Box.tsx","../../../../foundation/primitives-native/src/Text.tsx","../../../../foundation/primitives-native/src/index.tsx"],"sourcesContent":["export * from \"./ImageUploader\";\n","import React, { useRef, useState, useEffect, forwardRef } from \"react\";\n// @ts-expect-error - this will be resolved at build time\nimport { Box, Text, isWeb } from \"@xsolla/xui-primitives\";\nimport {\n useId,\n useResolvedTheme,\n type ThemeOverrideProps,\n} from \"@xsolla/xui-core\";\nimport { Image, TrashCan } from \"@xsolla/xui-icons-base\";\nimport { Spinner } from \"@xsolla/xui-spinner\";\n\nexport type ImageUploaderSize = \"xl\" | \"lg\" | \"md\" | \"sm\" | \"xs\";\n\n/** Controlled value shape. */\nexport interface ImageUploaderValue {\n filename?: string;\n url?: string;\n}\n\n/**\n * Normalized file shape produced by the platform picker / drag-drop pipeline.\n * - `uri` is a data-URL on web and a file URI on native.\n * - On web, the original `File` is passed through for consumers that need it\n * (e.g. to upload via FormData / fetch).\n */\nexport type ImageUploaderFile = {\n name: string;\n size: number;\n uri: string;\n mimeType?: string;\n /** The original DOM File (web only) */\n file?: File;\n};\n\nexport interface ImageUploaderProps extends ThemeOverrideProps {\n /** Size of the uploader. Figma default is `xl`. */\n size?: ImageUploaderSize;\n /** Placeholder shown under the icon. Accepts a string or a custom React node. */\n placeholder?: React.ReactNode;\n /** Placeholder shown while the file is uploading. Accepts a string or a custom React node. */\n uploadingPlaceholder?: React.ReactNode;\n /** Description below the placeholder. Only rendered when `wideView` is true. Accepts a string or a custom React node. */\n description?: React.ReactNode;\n /** Error message — when provided, component renders in the error state. */\n errorMessage?: string;\n /** Wide view (horizontal layout). When true, the box stretches to its parent's full width. */\n wideView?: boolean;\n /** Disabled state. */\n disabled?: boolean;\n /** Controlled loading state (shows spinner + \"Uploading\" label). */\n loading?: boolean;\n\n /** Controlled value. When provided, the component reflects this value. */\n value?: ImageUploaderValue | null;\n\n /**\n * Fires when the user picks (or drops) a file. Use this to perform the\n * actual upload — the component itself does no I/O.\n *\n * If the handler returns a `Promise`:\n * - The component automatically shows the uploading state (spinner +\n * `uploadingPlaceholder`) until the promise settles.\n * - If the promise resolves to an `ImageUploaderValue` (`{url, filename?}`)\n * or a `string` URL, the component automatically calls `onChange(value)`\n * with that value. The string form is sugar for `{url: <string>}`.\n * - If the promise rejects, the component calls `onChange(null, error)`.\n *\n * For finer-grained control, pass `loading` explicitly instead.\n */\n onUpload?: (\n file: ImageUploaderFile\n ) => void | Promise<ImageUploaderValue | string | void>;\n /**\n * Fires when the displayed value changes — on file pick (with a local\n * data-URL preview), after upload resolves (with the server value), or on\n * remove (`null`). The optional second argument carries any error thrown by\n * the consumer's `onUpload`.\n */\n onChange?: (value: ImageUploaderValue | null, error?: Error) => void;\n /** Fires when the user removes the image (trash click / clear). */\n onDelete?: () => void;\n\n /**\n * Accepted file types — passed through to the hidden `<input accept>` as a\n * picker hint (web only — ignored on native). Standard HTML syntax:\n * comma-separated list of MIME types (`image/png`), MIME wildcards\n * (`image/*`), or file extensions (`.png`). Defaults to `image/*`.\n *\n * No runtime validation is performed — the consumer's `onUpload` (or the\n * backend it calls) is responsible for accepting/rejecting files.\n */\n accept?: string;\n /**\n * Native file picker hook. Required on native (no DOM `<input type=\"file\">`).\n * On web, omit this and the component falls back to a hidden file input.\n */\n openPicker?: () => Promise<ImageUploaderFile | null>;\n /** Test ID for testing frameworks */\n testID?: string;\n}\n\ntype InternalState =\n | \"default\"\n | \"hover\"\n | \"focus\"\n | \"uploading\"\n | \"uploaded\"\n | \"uploadedHover\"\n | \"error\"\n | \"disable\";\n\nexport const ImageUploader = forwardRef<HTMLInputElement, ImageUploaderProps>(\n (\n {\n size = \"xl\",\n placeholder = \"Upload\",\n uploadingPlaceholder = \"Uploading\",\n description,\n errorMessage,\n wideView = false,\n accept = \"image/*\",\n openPicker,\n value,\n onUpload,\n onChange,\n onDelete,\n disabled = false,\n loading = false,\n testID,\n themeMode,\n themeProductContext,\n },\n ref\n ) => {\n const { theme } = useResolvedTheme({ themeMode, themeProductContext });\n const fileInputRef = useRef<HTMLInputElement>(null);\n const isControlled = value !== undefined;\n const [internalPreview, setInternalPreview] = useState<string | null>(null);\n const [isAutoUploading, setIsAutoUploading] = useState(false);\n // Captures errors thrown by the consumer's `onUpload`. Surfaced via the\n // component's normal error UI when no explicit `errorMessage` prop is set.\n const [autoError, setAutoError] = useState<string | null>(null);\n const [isHover, setIsHover] = useState(false);\n const [isFocus, setIsFocus] = useState(false);\n const [isPreviewHover, setIsPreviewHover] = useState(false);\n // Touch / native devices have no hover, so the trash overlay would never\n // appear. Detect once on mount and force `uploadedHover` for those cases.\n const [isHoverless] = useState(\n () =>\n !isWeb ||\n (typeof window !== \"undefined\" &&\n !!window.matchMedia?.(\"(hover: none)\").matches)\n );\n\n const reactId = useId();\n const baseId = `image-uploader-${reactId.replace(/[^a-zA-Z0-9-]/g, \"\")}`;\n const descriptionId = `${baseId}-description`;\n const errorId = `${baseId}-error`;\n\n const preview = isControlled ? (value?.url ?? null) : internalPreview;\n\n useEffect(() => {\n if (!isControlled && value === null) setInternalPreview(null);\n }, [isControlled, value]);\n\n React.useImperativeHandle(\n ref,\n () => fileInputRef.current as HTMLInputElement,\n []\n );\n\n const sizeStyles = theme.sizing.imageUploader(size);\n const inputColors = theme.colors.control.input;\n const focusColors = theme.colors.control.focus;\n const alertBorder = theme.colors.control.alert.border;\n const alertText = theme.colors.content.alert.primary;\n const scrim = theme.colors.layer.scrim;\n\n // Resolved error message: explicit prop wins, fall back to caught upload error.\n const resolvedError = errorMessage || autoError;\n const hasError = !!resolvedError;\n\n let state: InternalState;\n if (disabled) state = \"disable\";\n else if (loading || isAutoUploading) state = \"uploading\";\n else if (hasError) state = \"error\";\n else if (preview)\n state = isPreviewHover || isHoverless ? \"uploadedHover\" : \"uploaded\";\n else if (isFocus) state = \"focus\";\n else if (isHover) state = \"hover\";\n else state = \"default\";\n\n const emitFile = (img: ImageUploaderFile) => {\n // Clear any prior error from a previous upload attempt.\n setAutoError(null);\n if (!isControlled) setInternalPreview(img.uri);\n onChange?.({\n filename: img.name,\n url: img.uri,\n });\n const result = onUpload?.(img);\n // If the consumer's onUpload returns a Promise:\n // - show the uploading state until it settles\n // - on resolve with a value, treat it as the canonical onChange payload\n // - on reject, surface the error internally AND via onChange(null, error)\n if (result && typeof (result as Promise<unknown>).then === \"function\") {\n setIsAutoUploading(true);\n Promise.resolve(result)\n .then((uploaded) => {\n // Accept either an ImageUploaderValue ({url, filename?}) or a\n // bare string URL as sugar for {url: <string>}.\n let next: ImageUploaderValue | null = null;\n if (typeof uploaded === \"string\") {\n next = { url: uploaded };\n } else if (\n uploaded &&\n typeof uploaded === \"object\" &&\n \"url\" in uploaded\n ) {\n next = uploaded as ImageUploaderValue;\n }\n if (next) {\n if (!isControlled) setInternalPreview(next.url ?? null);\n onChange?.(next);\n }\n })\n .catch((err: Error) => {\n // Surface internally so the error UI lights up without the\n // consumer having to wire up local state.\n setAutoError(err?.message || \"Upload failed\");\n // Reset preview so we don't keep showing the failed pick.\n if (!isControlled) setInternalPreview(null);\n onChange?.(null, err);\n })\n .finally(() => setIsAutoUploading(false));\n }\n };\n\n const handleWebFile = (file: File) => {\n const reader = new FileReader();\n reader.onload = (e) => {\n const uri = e.target?.result as string;\n // Mutate the DOM File so it satisfies BOTH the DOM File interface (for\n // direct use in FormData / fetch / .size checks) and ImageUploaderFile\n // (with .uri / .mimeType added). `.file` self-references for back-compat\n // with consumers that previously dereferenced `imageFile.file`.\n const enriched = file as File & ImageUploaderFile;\n (enriched as { uri?: string }).uri = uri;\n (enriched as { mimeType?: string }).mimeType = file.type;\n (enriched as { file?: File }).file = enriched;\n emitFile(enriched);\n };\n reader.readAsDataURL(file);\n };\n\n const handleClick = async () => {\n if (disabled || loading) return;\n if (preview) {\n removeImage();\n return;\n }\n if (openPicker) {\n const picked = await openPicker();\n if (picked) emitFile(picked);\n return;\n }\n if (isWeb) {\n fileInputRef.current?.click();\n }\n };\n\n const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const file = e.target.files?.[0];\n e.target.value = \"\";\n if (file) handleWebFile(file);\n };\n\n const handleDragOver = (e: React.DragEvent) => {\n e.preventDefault();\n e.stopPropagation();\n if (!disabled && !loading && !preview) setIsHover(true);\n };\n\n const handleDragLeave = (e: React.DragEvent) => {\n e.preventDefault();\n e.stopPropagation();\n setIsHover(false);\n };\n\n const handleDrop = (e: React.DragEvent) => {\n e.preventDefault();\n e.stopPropagation();\n setIsHover(false);\n if (disabled || loading || preview) return;\n const file = e.dataTransfer.files?.[0];\n if (file) handleWebFile(file);\n };\n\n const removeImage = (e?: { stopPropagation?: () => void }) => {\n e?.stopPropagation?.();\n setAutoError(null);\n if (!isControlled) setInternalPreview(null);\n setIsPreviewHover(false);\n onDelete?.();\n onChange?.(null);\n if (isWeb && fileInputRef.current) fileInputRef.current.value = \"\";\n };\n\n let backgroundColor: string = inputColors.bg;\n let borderColor: string = inputColors.border;\n let borderStyle: \"dashed\" | \"solid\" = \"dashed\";\n let labelColor: string = inputColors.text;\n let descriptionColor: string = inputColors.placeholder;\n let iconColor: string = inputColors.text;\n\n switch (state) {\n case \"hover\":\n backgroundColor = inputColors.bgHover;\n borderColor = inputColors.borderHover;\n break;\n case \"focus\":\n case \"uploading\":\n backgroundColor = focusColors.bg;\n borderColor = focusColors.border;\n break;\n case \"uploaded\":\n case \"uploadedHover\":\n backgroundColor = \"transparent\";\n borderColor = \"transparent\";\n break;\n case \"error\":\n backgroundColor = inputColors.bg;\n borderColor = alertBorder;\n borderStyle = \"solid\";\n labelColor = alertText;\n iconColor = alertText;\n break;\n case \"disable\":\n backgroundColor = inputColors.bgDisable;\n borderColor = inputColors.borderDisable;\n labelColor = inputColors.textDisable;\n descriptionColor = inputColors.textDisable;\n iconColor = inputColors.textDisable;\n break;\n }\n\n const rootGap = state === \"error\" ? sizeStyles.errorGap : 0;\n const placeholderContent =\n state === \"uploading\" ? uploadingPlaceholder : placeholder;\n\n const renderInner = () => {\n if (state === \"uploaded\" || state === \"uploadedHover\") {\n return (\n <>\n <Box\n as=\"img\"\n src={preview || undefined}\n alt={value?.filename ?? \"Uploaded image\"}\n position=\"absolute\"\n top={0}\n left={0}\n right={0}\n bottom={0}\n borderRadius={sizeStyles.radius}\n resizeMode=\"cover\"\n {...(isWeb && { width: \"100%\", height: \"100%\" })}\n style={isWeb ? { objectFit: \"cover\" } : undefined}\n />\n {state === \"uploadedHover\" && !disabled && (\n <>\n <Box\n position=\"absolute\"\n top={0}\n left={0}\n right={0}\n bottom={0}\n backgroundColor={scrim}\n borderRadius={sizeStyles.radius}\n {...(isWeb && { \"aria-hidden\": \"true\" })}\n />\n <Box\n position=\"relative\"\n width={sizeStyles.iconSize}\n height={sizeStyles.iconSize}\n alignItems=\"center\"\n justifyContent=\"center\"\n zIndex={1}\n pointerEvents=\"none\"\n {...(isWeb && { \"aria-hidden\": \"true\" })}\n >\n <TrashCan size={sizeStyles.iconSize} color=\"#ffffff\" />\n </Box>\n </>\n )}\n </>\n );\n }\n\n return (\n <>\n {state === \"uploading\" ? (\n <Spinner\n size={size}\n color={theme.colors.content.brand.primary}\n {...(isWeb && { \"aria-hidden\": \"true\" })}\n />\n ) : (\n <Box {...(isWeb && { \"aria-hidden\": \"true\" })}>\n <Image size={sizeStyles.iconSize} color={iconColor} />\n </Box>\n )}\n\n {placeholderContent &&\n (typeof placeholderContent === \"string\" ? (\n <Text\n data-testid=\"image-uploader__placeholder\"\n color={labelColor}\n fontSize={sizeStyles.labelFontSize}\n lineHeight={sizeStyles.labelLineHeight}\n fontWeight=\"500\"\n textAlign=\"center\"\n numberOfLines={1}\n style={\n isWeb\n ? {\n maxWidth: \"100%\",\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n whiteSpace: \"nowrap\",\n }\n : undefined\n }\n >\n {placeholderContent}\n </Text>\n ) : (\n <Box data-testid=\"image-uploader__placeholder\">\n {placeholderContent}\n </Box>\n ))}\n\n {description &&\n wideView &&\n state !== \"uploading\" &&\n state !== \"error\" &&\n (typeof description === \"string\" ? (\n <Text\n data-testid=\"image-uploader__description\"\n color={descriptionColor}\n fontSize={sizeStyles.descriptionFontSize}\n lineHeight={sizeStyles.descriptionLineHeight}\n textAlign=\"center\"\n numberOfLines={1}\n style={\n isWeb\n ? {\n maxWidth: \"100%\",\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n whiteSpace: \"nowrap\",\n }\n : undefined\n }\n {...(isWeb && { id: descriptionId })}\n >\n {description}\n </Text>\n ) : (\n <Box\n data-testid=\"image-uploader__description\"\n {...(isWeb && { id: descriptionId })}\n >\n {description}\n </Box>\n ))}\n </>\n );\n };\n\n // Web-only DOM event handlers — stripped on native.\n const webOnlyHandlers = isWeb\n ? {\n onMouseEnter: () => {\n if (disabled || loading) return;\n if (preview) setIsPreviewHover(true);\n else setIsHover(true);\n },\n onMouseLeave: () => {\n if (preview) setIsPreviewHover(false);\n else setIsHover(false);\n },\n onFocus: (e: React.FocusEvent<HTMLElement>) => {\n if (disabled || loading || preview) return;\n // Only show the focus visual state for keyboard focus, not for\n // pointer focus (e.g. after the native file picker is cancelled).\n if (\n typeof e.target?.matches === \"function\" &&\n !e.target.matches(\":focus-visible\")\n ) {\n return;\n }\n setIsFocus(true);\n },\n onBlur: () => setIsFocus(false),\n onDragOver: handleDragOver,\n onDragLeave: handleDragLeave,\n onDrop: handleDrop,\n }\n : {};\n\n // Web-only CSS props — dropped on native to avoid style warnings.\n const webOnlyStyle = isWeb\n ? {\n boxSizing: \"border-box\" as const,\n display: \"flex\" as const,\n cursor: (disabled || loading\n ? \"not-allowed\"\n : \"pointer\") as React.CSSProperties[\"cursor\"],\n outline: \"none\",\n transition: \"background-color 0.15s ease, border-color 0.15s ease\",\n }\n : {};\n\n // aria-label needs a string; fall back to defaults when placeholder/uploadingPlaceholder\n // are passed as React nodes.\n const buttonAriaLabel = preview\n ? `Remove image${value?.filename ? `: ${value.filename}` : \"\"}`\n : state === \"uploading\"\n ? typeof uploadingPlaceholder === \"string\"\n ? uploadingPlaceholder\n : \"Uploading\"\n : typeof placeholder === \"string\"\n ? placeholder\n : \"Upload\";\n\n const describedBy =\n [\n state === \"error\" && resolvedError ? errorId : null,\n description && wideView && state !== \"uploading\" && state !== \"error\"\n ? descriptionId\n : null,\n ]\n .filter(Boolean)\n .join(\" \") || undefined;\n\n return (\n <Box\n testID={testID || \"image-uploader\"}\n flexDirection=\"column\"\n gap={rootGap}\n alignItems=\"flex-start\"\n width={wideView ? \"100%\" : undefined}\n >\n {isWeb && (\n <input\n type=\"file\"\n ref={fileInputRef}\n accept={accept}\n onChange={handleFileChange}\n style={{ display: \"none\" }}\n disabled={disabled}\n tabIndex={-1}\n aria-hidden=\"true\"\n data-testid=\"image-uploader__input\"\n />\n )}\n\n <Box\n as={isWeb ? \"button\" : \"div\"}\n disabled={disabled || loading}\n data-testid=\"image-uploader__button\"\n onPress={handleClick}\n {...(isWeb && {\n \"aria-label\": buttonAriaLabel,\n \"aria-busy\": loading || undefined,\n \"aria-invalid\": state === \"error\" || undefined,\n \"aria-describedby\": describedBy,\n })}\n {...webOnlyHandlers}\n position=\"relative\"\n width={wideView ? \"100%\" : sizeStyles.box}\n height={sizeStyles.box}\n flexDirection=\"column\"\n alignItems=\"center\"\n justifyContent=\"center\"\n gap={sizeStyles.gap}\n padding={state === \"uploaded\" ? 0 : sizeStyles.padding}\n borderWidth={sizeStyles.borderWidth}\n borderStyle={borderStyle}\n borderColor={borderColor}\n borderRadius={sizeStyles.radius}\n backgroundColor={backgroundColor}\n overflow=\"hidden\"\n style={webOnlyStyle}\n >\n {renderInner()}\n </Box>\n\n {state === \"error\" && resolvedError && (\n <Text\n data-testid=\"image-uploader__error\"\n color={alertText}\n fontSize={sizeStyles.errorFontSize}\n lineHeight={sizeStyles.errorLineHeight}\n {...(isWeb && { id: errorId, role: \"alert\" })}\n >\n {resolvedError}\n </Text>\n )}\n </Box>\n );\n }\n);\n\nImageUploader.displayName = \"ImageUploader\";\n","import React from \"react\";\nimport {\n View,\n Pressable,\n Image,\n ViewStyle,\n ImageStyle,\n DimensionValue,\n AnimatableNumericValue,\n} from \"react-native\";\nimport { BoxProps } from \"@xsolla/xui-primitives-core\";\n\nexport const Box: React.FC<BoxProps> = ({\n children,\n onPress,\n onLayout,\n onMoveShouldSetResponder,\n onResponderGrant,\n onResponderMove,\n onResponderRelease,\n onResponderTerminate,\n backgroundColor,\n borderColor,\n borderWidth,\n borderBottomWidth,\n borderBottomColor,\n borderTopWidth,\n borderTopColor,\n borderLeftWidth,\n borderLeftColor,\n borderRightWidth,\n borderRightColor,\n borderRadius,\n borderStyle,\n height,\n padding,\n paddingHorizontal,\n paddingVertical,\n margin,\n marginTop,\n marginBottom,\n marginLeft,\n marginRight,\n flexDirection,\n alignItems,\n justifyContent,\n position,\n top,\n bottom,\n left,\n right,\n width,\n minWidth,\n minHeight,\n maxWidth,\n maxHeight,\n flex,\n overflow,\n zIndex,\n hoverStyle,\n pressStyle,\n style,\n \"data-testid\": dataTestId,\n testID,\n as,\n src,\n alt,\n ...rest\n}) => {\n const getContainerStyle = (pressed?: boolean): ViewStyle => ({\n backgroundColor:\n pressed && pressStyle?.backgroundColor\n ? pressStyle.backgroundColor\n : backgroundColor,\n borderColor,\n borderWidth,\n borderBottomWidth,\n borderBottomColor,\n borderTopWidth,\n borderTopColor,\n borderLeftWidth,\n borderLeftColor,\n borderRightWidth,\n borderRightColor,\n borderRadius: borderRadius as AnimatableNumericValue,\n borderStyle: borderStyle as ViewStyle[\"borderStyle\"],\n overflow,\n zIndex,\n height: height as DimensionValue,\n width: width as DimensionValue,\n minWidth: minWidth as DimensionValue,\n minHeight: minHeight as DimensionValue,\n maxWidth: maxWidth as DimensionValue,\n maxHeight: maxHeight as DimensionValue,\n padding: padding as DimensionValue,\n paddingHorizontal: paddingHorizontal as DimensionValue,\n paddingVertical: paddingVertical as DimensionValue,\n margin: margin as DimensionValue,\n marginTop: marginTop as DimensionValue,\n marginBottom: marginBottom as DimensionValue,\n marginLeft: marginLeft as DimensionValue,\n marginRight: marginRight as DimensionValue,\n flexDirection,\n alignItems,\n justifyContent,\n position: position as ViewStyle[\"position\"],\n top: top as DimensionValue,\n bottom: bottom as DimensionValue,\n left: left as DimensionValue,\n right: right as DimensionValue,\n flex,\n ...(style as ViewStyle),\n });\n\n const finalTestID = dataTestId || testID;\n\n // Destructure and drop web-only props from rest before passing to RN components\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const {\n role,\n tabIndex,\n onKeyDown,\n onKeyUp,\n \"aria-label\": _ariaLabel,\n \"aria-labelledby\": _ariaLabelledBy,\n \"aria-current\": _ariaCurrent,\n \"aria-disabled\": _ariaDisabled,\n \"aria-live\": _ariaLive,\n className,\n \"data-testid\": _dataTestId,\n ...nativeRest\n } = rest as Record<string, unknown>;\n\n // Handle as=\"img\" for React Native\n if (as === \"img\" && src) {\n const imageStyle: ImageStyle = {\n width: width as DimensionValue,\n height: height as DimensionValue,\n borderRadius: borderRadius as number,\n position: position as ImageStyle[\"position\"],\n top: top as DimensionValue,\n bottom: bottom as DimensionValue,\n left: left as DimensionValue,\n right: right as DimensionValue,\n ...(style as ImageStyle),\n };\n\n return (\n <Image\n source={{ uri: src }}\n style={imageStyle}\n testID={finalTestID}\n resizeMode=\"cover\"\n {...nativeRest}\n />\n );\n }\n\n if (onPress) {\n return (\n <Pressable\n onPress={onPress}\n onLayout={onLayout}\n onMoveShouldSetResponder={onMoveShouldSetResponder}\n onResponderGrant={onResponderGrant}\n onResponderMove={onResponderMove}\n onResponderRelease={onResponderRelease}\n onResponderTerminate={onResponderTerminate}\n style={({ pressed }) => getContainerStyle(pressed)}\n testID={finalTestID}\n {...nativeRest}\n >\n {children}\n </Pressable>\n );\n }\n\n return (\n <View\n style={getContainerStyle()}\n testID={finalTestID}\n onLayout={onLayout}\n onMoveShouldSetResponder={onMoveShouldSetResponder}\n onResponderGrant={onResponderGrant}\n onResponderMove={onResponderMove}\n onResponderRelease={onResponderRelease}\n onResponderTerminate={onResponderTerminate}\n {...nativeRest}\n >\n {children}\n </View>\n );\n};\n","import React from \"react\";\nimport {\n Text as RNText,\n TextStyle,\n AccessibilityRole,\n StyleSheet,\n} from \"react-native\";\nimport { TextProps } from \"@xsolla/xui-primitives-core\";\n\nconst roleMap: Record<string, AccessibilityRole> = {\n alert: \"alert\",\n heading: \"header\",\n button: \"button\",\n link: \"link\",\n text: \"text\",\n};\n\nconst parseNumericValue = (\n value: string | number | undefined\n): number | undefined => {\n if (value === undefined) return undefined;\n if (typeof value === \"number\") return value;\n const parsed = parseFloat(value);\n return isNaN(parsed) ? undefined : parsed;\n};\n\nexport const Text: React.FC<TextProps> = ({\n children,\n color,\n fontSize,\n fontWeight,\n fontFamily,\n textAlign,\n lineHeight,\n numberOfLines,\n id,\n role,\n testID,\n \"data-testid\": dataTestId,\n style: styleProp,\n ...props\n}) => {\n let resolvedFontFamily = fontFamily\n ? fontFamily.split(\",\")[0].replace(/['\"]/g, \"\").trim()\n : undefined;\n\n if (\n resolvedFontFamily === \"Pilat Wide\" ||\n resolvedFontFamily === \"Pilat Wide Bold\" ||\n resolvedFontFamily === \"Aktiv Grotesk\"\n ) {\n resolvedFontFamily = undefined;\n }\n\n const incomingStyle = StyleSheet.flatten(styleProp) as TextStyle | undefined;\n\n const baseStyle: TextStyle = {\n color: color ?? incomingStyle?.color,\n fontSize: typeof fontSize === \"number\" ? fontSize : undefined,\n fontWeight: fontWeight as TextStyle[\"fontWeight\"],\n fontFamily: resolvedFontFamily,\n textDecorationLine: props.textDecoration as TextStyle[\"textDecorationLine\"],\n textAlign: textAlign ?? incomingStyle?.textAlign,\n lineHeight: parseNumericValue(lineHeight ?? incomingStyle?.lineHeight),\n marginTop: parseNumericValue(\n incomingStyle?.marginTop as number | string | undefined\n ),\n marginBottom: parseNumericValue(\n incomingStyle?.marginBottom as number | string | undefined\n ),\n };\n\n const accessibilityRole = role ? roleMap[role] : undefined;\n\n return (\n <RNText\n style={baseStyle}\n numberOfLines={numberOfLines}\n testID={dataTestId || testID || id}\n accessibilityRole={accessibilityRole}\n >\n {children}\n </RNText>\n );\n};\n","export * from \"./Box\";\nexport * from \"./Text\";\nexport * from \"./Spinner\";\nexport * from \"./Icon\";\nexport * from \"./Divider\";\nexport * from \"./Input\";\nexport * from \"./TextArea\";\nexport * from \"./LinearGradient\";\n\nexport const isWeb = false;\nexport const isNative = true;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,mBAA+D;;;ACC/D,0BAQO;AA2ID;AAxIC,IAAM,MAA0B,CAAC;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,eAAe;AAAA,EACf;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,MAAM;AACJ,QAAM,oBAAoB,CAAC,aAAkC;AAAA,IAC3D,iBACE,WAAW,YAAY,kBACnB,WAAW,kBACX;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAI;AAAA,EACN;AAEA,QAAM,cAAc,cAAc;AAIlC,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,cAAc;AAAA,IACd,mBAAmB;AAAA,IACnB,gBAAgB;AAAA,IAChB,iBAAiB;AAAA,IACjB,aAAa;AAAA,IACb;AAAA,IACA,eAAe;AAAA,IACf,GAAG;AAAA,EACL,IAAI;AAGJ,MAAI,OAAO,SAAS,KAAK;AACvB,UAAM,aAAyB;AAAA,MAC7B;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,GAAI;AAAA,IACN;AAEA,WACE;AAAA,MAAC;AAAA;AAAA,QACC,QAAQ,EAAE,KAAK,IAAI;AAAA,QACnB,OAAO;AAAA,QACP,QAAQ;AAAA,QACR,YAAW;AAAA,QACV,GAAG;AAAA;AAAA,IACN;AAAA,EAEJ;AAEA,MAAI,SAAS;AACX,WACE;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,OAAO,CAAC,EAAE,QAAQ,MAAM,kBAAkB,OAAO;AAAA,QACjD,QAAQ;AAAA,QACP,GAAG;AAAA,QAEH;AAAA;AAAA,IACH;AAAA,EAEJ;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,OAAO,kBAAkB;AAAA,MACzB,QAAQ;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACC,GAAG;AAAA,MAEH;AAAA;AAAA,EACH;AAEJ;;;AC/LA,IAAAA,uBAKO;AAqEH,IAAAC,sBAAA;AAlEJ,IAAM,UAA6C;AAAA,EACjD,OAAO;AAAA,EACP,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,MAAM;AACR;AAEA,IAAM,oBAAoB,CACxB,UACuB;AACvB,MAAI,UAAU,OAAW,QAAO;AAChC,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,QAAM,SAAS,WAAW,KAAK;AAC/B,SAAO,MAAM,MAAM,IAAI,SAAY;AACrC;AAEO,IAAM,OAA4B,CAAC;AAAA,EACxC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,eAAe;AAAA,EACf,OAAO;AAAA,EACP,GAAG;AACL,MAAM;AACJ,MAAI,qBAAqB,aACrB,WAAW,MAAM,GAAG,EAAE,CAAC,EAAE,QAAQ,SAAS,EAAE,EAAE,KAAK,IACnD;AAEJ,MACE,uBAAuB,gBACvB,uBAAuB,qBACvB,uBAAuB,iBACvB;AACA,yBAAqB;AAAA,EACvB;AAEA,QAAM,gBAAgB,gCAAW,QAAQ,SAAS;AAElD,QAAM,YAAuB;AAAA,IAC3B,OAAO,SAAS,eAAe;AAAA,IAC/B,UAAU,OAAO,aAAa,WAAW,WAAW;AAAA,IACpD;AAAA,IACA,YAAY;AAAA,IACZ,oBAAoB,MAAM;AAAA,IAC1B,WAAW,aAAa,eAAe;AAAA,IACvC,YAAY,kBAAkB,cAAc,eAAe,UAAU;AAAA,IACrE,WAAW;AAAA,MACT,eAAe;AAAA,IACjB;AAAA,IACA,cAAc;AAAA,MACZ,eAAe;AAAA,IACjB;AAAA,EACF;AAEA,QAAM,oBAAoB,OAAO,QAAQ,IAAI,IAAI;AAEjD,SACE;AAAA,IAAC,qBAAAC;AAAA,IAAA;AAAA,MACC,OAAO;AAAA,MACP;AAAA,MACA,QAAQ,cAAc,UAAU;AAAA,MAChC;AAAA,MAEC;AAAA;AAAA,EACH;AAEJ;;;AC3EO,IAAM,QAAQ;;;AHNrB,sBAIO;AACP,4BAAgC;AAChC,yBAAwB;AAyVZ,IAAAC,sBAAA;AAnPL,IAAM,oBAAgB;AAAA,EAC3B,CACE;AAAA,IACE,OAAO;AAAA,IACP,cAAc;AAAA,IACd,uBAAuB;AAAA,IACvB;AAAA,IACA;AAAA,IACA,WAAW;AAAA,IACX,SAAS;AAAA,IACT;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,WAAW;AAAA,IACX,UAAU;AAAA,IACV;AAAA,IACA;AAAA,IACA;AAAA,EACF,GACA,QACG;AACH,UAAM,EAAE,MAAM,QAAI,kCAAiB,EAAE,WAAW,oBAAoB,CAAC;AACrE,UAAM,mBAAe,qBAAyB,IAAI;AAClD,UAAM,eAAe,UAAU;AAC/B,UAAM,CAAC,iBAAiB,kBAAkB,QAAI,uBAAwB,IAAI;AAC1E,UAAM,CAAC,iBAAiB,kBAAkB,QAAI,uBAAS,KAAK;AAG5D,UAAM,CAAC,WAAW,YAAY,QAAI,uBAAwB,IAAI;AAC9D,UAAM,CAAC,SAAS,UAAU,QAAI,uBAAS,KAAK;AAC5C,UAAM,CAAC,SAAS,UAAU,QAAI,uBAAS,KAAK;AAC5C,UAAM,CAAC,gBAAgB,iBAAiB,QAAI,uBAAS,KAAK;AAG1D,UAAM,CAAC,WAAW,QAAI;AAAA,MACpB,MACE,CAAC,SACA,OAAO,WAAW,eACjB,CAAC,CAAC,OAAO,aAAa,eAAe,EAAE;AAAA,IAC7C;AAEA,UAAM,cAAU,uBAAM;AACtB,UAAM,SAAS,kBAAkB,QAAQ,QAAQ,kBAAkB,EAAE,CAAC;AACtE,UAAM,gBAAgB,GAAG,MAAM;AAC/B,UAAM,UAAU,GAAG,MAAM;AAEzB,UAAM,UAAU,eAAgB,OAAO,OAAO,OAAQ;AAEtD,gCAAU,MAAM;AACd,UAAI,CAAC,gBAAgB,UAAU,KAAM,oBAAmB,IAAI;AAAA,IAC9D,GAAG,CAAC,cAAc,KAAK,CAAC;AAExB,iBAAAC,QAAM;AAAA,MACJ;AAAA,MACA,MAAM,aAAa;AAAA,MACnB,CAAC;AAAA,IACH;AAEA,UAAM,aAAa,MAAM,OAAO,cAAc,IAAI;AAClD,UAAM,cAAc,MAAM,OAAO,QAAQ;AACzC,UAAM,cAAc,MAAM,OAAO,QAAQ;AACzC,UAAM,cAAc,MAAM,OAAO,QAAQ,MAAM;AAC/C,UAAM,YAAY,MAAM,OAAO,QAAQ,MAAM;AAC7C,UAAM,QAAQ,MAAM,OAAO,MAAM;AAGjC,UAAM,gBAAgB,gBAAgB;AACtC,UAAM,WAAW,CAAC,CAAC;AAEnB,QAAI;AACJ,QAAI,SAAU,SAAQ;AAAA,aACb,WAAW,gBAAiB,SAAQ;AAAA,aACpC,SAAU,SAAQ;AAAA,aAClB;AACP,cAAQ,kBAAkB,cAAc,kBAAkB;AAAA,aACnD,QAAS,SAAQ;AAAA,aACjB,QAAS,SAAQ;AAAA,QACrB,SAAQ;AAEb,UAAM,WAAW,CAAC,QAA2B;AAE3C,mBAAa,IAAI;AACjB,UAAI,CAAC,aAAc,oBAAmB,IAAI,GAAG;AAC7C,iBAAW;AAAA,QACT,UAAU,IAAI;AAAA,QACd,KAAK,IAAI;AAAA,MACX,CAAC;AACD,YAAM,SAAS,WAAW,GAAG;AAK7B,UAAI,UAAU,OAAQ,OAA4B,SAAS,YAAY;AACrE,2BAAmB,IAAI;AACvB,gBAAQ,QAAQ,MAAM,EACnB,KAAK,CAAC,aAAa;AAGlB,cAAI,OAAkC;AACtC,cAAI,OAAO,aAAa,UAAU;AAChC,mBAAO,EAAE,KAAK,SAAS;AAAA,UACzB,WACE,YACA,OAAO,aAAa,YACpB,SAAS,UACT;AACA,mBAAO;AAAA,UACT;AACA,cAAI,MAAM;AACR,gBAAI,CAAC,aAAc,oBAAmB,KAAK,OAAO,IAAI;AACtD,uBAAW,IAAI;AAAA,UACjB;AAAA,QACF,CAAC,EACA,MAAM,CAAC,QAAe;AAGrB,uBAAa,KAAK,WAAW,eAAe;AAE5C,cAAI,CAAC,aAAc,oBAAmB,IAAI;AAC1C,qBAAW,MAAM,GAAG;AAAA,QACtB,CAAC,EACA,QAAQ,MAAM,mBAAmB,KAAK,CAAC;AAAA,MAC5C;AAAA,IACF;AAEA,UAAM,gBAAgB,CAAC,SAAe;AACpC,YAAM,SAAS,IAAI,WAAW;AAC9B,aAAO,SAAS,CAAC,MAAM;AACrB,cAAM,MAAM,EAAE,QAAQ;AAKtB,cAAM,WAAW;AACjB,QAAC,SAA8B,MAAM;AACrC,QAAC,SAAmC,WAAW,KAAK;AACpD,QAAC,SAA6B,OAAO;AACrC,iBAAS,QAAQ;AAAA,MACnB;AACA,aAAO,cAAc,IAAI;AAAA,IAC3B;AAEA,UAAM,cAAc,YAAY;AAC9B,UAAI,YAAY,QAAS;AACzB,UAAI,SAAS;AACX,oBAAY;AACZ;AAAA,MACF;AACA,UAAI,YAAY;AACd,cAAM,SAAS,MAAM,WAAW;AAChC,YAAI,OAAQ,UAAS,MAAM;AAC3B;AAAA,MACF;AACA,UAAI,OAAO;AACT,qBAAa,SAAS,MAAM;AAAA,MAC9B;AAAA,IACF;AAEA,UAAM,mBAAmB,CAAC,MAA2C;AACnE,YAAM,OAAO,EAAE,OAAO,QAAQ,CAAC;AAC/B,QAAE,OAAO,QAAQ;AACjB,UAAI,KAAM,eAAc,IAAI;AAAA,IAC9B;AAEA,UAAM,iBAAiB,CAAC,MAAuB;AAC7C,QAAE,eAAe;AACjB,QAAE,gBAAgB;AAClB,UAAI,CAAC,YAAY,CAAC,WAAW,CAAC,QAAS,YAAW,IAAI;AAAA,IACxD;AAEA,UAAM,kBAAkB,CAAC,MAAuB;AAC9C,QAAE,eAAe;AACjB,QAAE,gBAAgB;AAClB,iBAAW,KAAK;AAAA,IAClB;AAEA,UAAM,aAAa,CAAC,MAAuB;AACzC,QAAE,eAAe;AACjB,QAAE,gBAAgB;AAClB,iBAAW,KAAK;AAChB,UAAI,YAAY,WAAW,QAAS;AACpC,YAAM,OAAO,EAAE,aAAa,QAAQ,CAAC;AACrC,UAAI,KAAM,eAAc,IAAI;AAAA,IAC9B;AAEA,UAAM,cAAc,CAAC,MAAyC;AAC5D,SAAG,kBAAkB;AACrB,mBAAa,IAAI;AACjB,UAAI,CAAC,aAAc,oBAAmB,IAAI;AAC1C,wBAAkB,KAAK;AACvB,iBAAW;AACX,iBAAW,IAAI;AACf,UAAI,SAAS,aAAa,QAAS,cAAa,QAAQ,QAAQ;AAAA,IAClE;AAEA,QAAI,kBAA0B,YAAY;AAC1C,QAAI,cAAsB,YAAY;AACtC,QAAI,cAAkC;AACtC,QAAI,aAAqB,YAAY;AACrC,QAAI,mBAA2B,YAAY;AAC3C,QAAI,YAAoB,YAAY;AAEpC,YAAQ,OAAO;AAAA,MACb,KAAK;AACH,0BAAkB,YAAY;AAC9B,sBAAc,YAAY;AAC1B;AAAA,MACF,KAAK;AAAA,MACL,KAAK;AACH,0BAAkB,YAAY;AAC9B,sBAAc,YAAY;AAC1B;AAAA,MACF,KAAK;AAAA,MACL,KAAK;AACH,0BAAkB;AAClB,sBAAc;AACd;AAAA,MACF,KAAK;AACH,0BAAkB,YAAY;AAC9B,sBAAc;AACd,sBAAc;AACd,qBAAa;AACb,oBAAY;AACZ;AAAA,MACF,KAAK;AACH,0BAAkB,YAAY;AAC9B,sBAAc,YAAY;AAC1B,qBAAa,YAAY;AACzB,2BAAmB,YAAY;AAC/B,oBAAY,YAAY;AACxB;AAAA,IACJ;AAEA,UAAM,UAAU,UAAU,UAAU,WAAW,WAAW;AAC1D,UAAM,qBACJ,UAAU,cAAc,uBAAuB;AAEjD,UAAM,cAAc,MAAM;AACxB,UAAI,UAAU,cAAc,UAAU,iBAAiB;AACrD,eACE,8EACE;AAAA;AAAA,YAAC;AAAA;AAAA,cACC,IAAG;AAAA,cACH,KAAK,WAAW;AAAA,cAChB,KAAK,OAAO,YAAY;AAAA,cACxB,UAAS;AAAA,cACT,KAAK;AAAA,cACL,MAAM;AAAA,cACN,OAAO;AAAA,cACP,QAAQ;AAAA,cACR,cAAc,WAAW;AAAA,cACzB,YAAW;AAAA,cACV,GAAI,SAAS,EAAE,OAAO,QAAQ,QAAQ,OAAO;AAAA,cAC9C,OAAO,QAAQ,EAAE,WAAW,QAAQ,IAAI;AAAA;AAAA,UAC1C;AAAA,UACC,UAAU,mBAAmB,CAAC,YAC7B,8EACE;AAAA;AAAA,cAAC;AAAA;AAAA,gBACC,UAAS;AAAA,gBACT,KAAK;AAAA,gBACL,MAAM;AAAA,gBACN,OAAO;AAAA,gBACP,QAAQ;AAAA,gBACR,iBAAiB;AAAA,gBACjB,cAAc,WAAW;AAAA,gBACxB,GAAI,SAAS,EAAE,eAAe,OAAO;AAAA;AAAA,YACxC;AAAA,YACA;AAAA,cAAC;AAAA;AAAA,gBACC,UAAS;AAAA,gBACT,OAAO,WAAW;AAAA,gBAClB,QAAQ,WAAW;AAAA,gBACnB,YAAW;AAAA,gBACX,gBAAe;AAAA,gBACf,QAAQ;AAAA,gBACR,eAAc;AAAA,gBACb,GAAI,SAAS,EAAE,eAAe,OAAO;AAAA,gBAEtC,uDAAC,kCAAS,MAAM,WAAW,UAAU,OAAM,WAAU;AAAA;AAAA,YACvD;AAAA,aACF;AAAA,WAEJ;AAAA,MAEJ;AAEA,aACE,8EACG;AAAA,kBAAU,cACT;AAAA,UAAC;AAAA;AAAA,YACC;AAAA,YACA,OAAO,MAAM,OAAO,QAAQ,MAAM;AAAA,YACjC,GAAI,SAAS,EAAE,eAAe,OAAO;AAAA;AAAA,QACxC,IAEA,6CAAC,OAAK,GAAI,SAAS,EAAE,eAAe,OAAO,GACzC,uDAAC,+BAAM,MAAM,WAAW,UAAU,OAAO,WAAW,GACtD;AAAA,QAGD,uBACE,OAAO,uBAAuB,WAC7B;AAAA,UAAC;AAAA;AAAA,YACC,eAAY;AAAA,YACZ,OAAO;AAAA,YACP,UAAU,WAAW;AAAA,YACrB,YAAY,WAAW;AAAA,YACvB,YAAW;AAAA,YACX,WAAU;AAAA,YACV,eAAe;AAAA,YACf,OACE,QACI;AAAA,cACE,UAAU;AAAA,cACV,UAAU;AAAA,cACV,cAAc;AAAA,cACd,YAAY;AAAA,YACd,IACA;AAAA,YAGL;AAAA;AAAA,QACH,IAEA,6CAAC,OAAI,eAAY,+BACd,8BACH;AAAA,QAGH,eACC,YACA,UAAU,eACV,UAAU,YACT,OAAO,gBAAgB,WACtB;AAAA,UAAC;AAAA;AAAA,YACC,eAAY;AAAA,YACZ,OAAO;AAAA,YACP,UAAU,WAAW;AAAA,YACrB,YAAY,WAAW;AAAA,YACvB,WAAU;AAAA,YACV,eAAe;AAAA,YACf,OACE,QACI;AAAA,cACE,UAAU;AAAA,cACV,UAAU;AAAA,cACV,cAAc;AAAA,cACd,YAAY;AAAA,YACd,IACA;AAAA,YAEL,GAAI,SAAS,EAAE,IAAI,cAAc;AAAA,YAEjC;AAAA;AAAA,QACH,IAEA;AAAA,UAAC;AAAA;AAAA,YACC,eAAY;AAAA,YACX,GAAI,SAAS,EAAE,IAAI,cAAc;AAAA,YAEjC;AAAA;AAAA,QACH;AAAA,SAEN;AAAA,IAEJ;AAGA,UAAM,kBAAkB,QACpB;AAAA,MACE,cAAc,MAAM;AAClB,YAAI,YAAY,QAAS;AACzB,YAAI,QAAS,mBAAkB,IAAI;AAAA,YAC9B,YAAW,IAAI;AAAA,MACtB;AAAA,MACA,cAAc,MAAM;AAClB,YAAI,QAAS,mBAAkB,KAAK;AAAA,YAC/B,YAAW,KAAK;AAAA,MACvB;AAAA,MACA,SAAS,CAAC,MAAqC;AAC7C,YAAI,YAAY,WAAW,QAAS;AAGpC,YACE,OAAO,EAAE,QAAQ,YAAY,cAC7B,CAAC,EAAE,OAAO,QAAQ,gBAAgB,GAClC;AACA;AAAA,QACF;AACA,mBAAW,IAAI;AAAA,MACjB;AAAA,MACA,QAAQ,MAAM,WAAW,KAAK;AAAA,MAC9B,YAAY;AAAA,MACZ,aAAa;AAAA,MACb,QAAQ;AAAA,IACV,IACA,CAAC;AAGL,UAAM,eAAe,QACjB;AAAA,MACE,WAAW;AAAA,MACX,SAAS;AAAA,MACT,QAAS,YAAY,UACjB,gBACA;AAAA,MACJ,SAAS;AAAA,MACT,YAAY;AAAA,IACd,IACA,CAAC;AAIL,UAAM,kBAAkB,UACpB,eAAe,OAAO,WAAW,KAAK,MAAM,QAAQ,KAAK,EAAE,KAC3D,UAAU,cACR,OAAO,yBAAyB,WAC9B,uBACA,cACF,OAAO,gBAAgB,WACrB,cACA;AAER,UAAM,cACJ;AAAA,MACE,UAAU,WAAW,gBAAgB,UAAU;AAAA,MAC/C,eAAe,YAAY,UAAU,eAAe,UAAU,UAC1D,gBACA;AAAA,IACN,EACG,OAAO,OAAO,EACd,KAAK,GAAG,KAAK;AAElB,WACE;AAAA,MAAC;AAAA;AAAA,QACC,QAAQ,UAAU;AAAA,QAClB,eAAc;AAAA,QACd,KAAK;AAAA,QACL,YAAW;AAAA,QACX,OAAO,WAAW,SAAS;AAAA,QAE1B;AAAA,mBACC;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,KAAK;AAAA,cACL;AAAA,cACA,UAAU;AAAA,cACV,OAAO,EAAE,SAAS,OAAO;AAAA,cACzB;AAAA,cACA,UAAU;AAAA,cACV,eAAY;AAAA,cACZ,eAAY;AAAA;AAAA,UACd;AAAA,UAGF;AAAA,YAAC;AAAA;AAAA,cACC,IAAI,QAAQ,WAAW;AAAA,cACvB,UAAU,YAAY;AAAA,cACtB,eAAY;AAAA,cACZ,SAAS;AAAA,cACR,GAAI,SAAS;AAAA,gBACZ,cAAc;AAAA,gBACd,aAAa,WAAW;AAAA,gBACxB,gBAAgB,UAAU,WAAW;AAAA,gBACrC,oBAAoB;AAAA,cACtB;AAAA,cACC,GAAG;AAAA,cACJ,UAAS;AAAA,cACT,OAAO,WAAW,SAAS,WAAW;AAAA,cACtC,QAAQ,WAAW;AAAA,cACnB,eAAc;AAAA,cACd,YAAW;AAAA,cACX,gBAAe;AAAA,cACf,KAAK,WAAW;AAAA,cAChB,SAAS,UAAU,aAAa,IAAI,WAAW;AAAA,cAC/C,aAAa,WAAW;AAAA,cACxB;AAAA,cACA;AAAA,cACA,cAAc,WAAW;AAAA,cACzB;AAAA,cACA,UAAS;AAAA,cACT,OAAO;AAAA,cAEN,sBAAY;AAAA;AAAA,UACf;AAAA,UAEC,UAAU,WAAW,iBACpB;AAAA,YAAC;AAAA;AAAA,cACC,eAAY;AAAA,cACZ,OAAO;AAAA,cACP,UAAU,WAAW;AAAA,cACrB,YAAY,WAAW;AAAA,cACtB,GAAI,SAAS,EAAE,IAAI,SAAS,MAAM,QAAQ;AAAA,cAE1C;AAAA;AAAA,UACH;AAAA;AAAA;AAAA,IAEJ;AAAA,EAEJ;AACF;AAEA,cAAc,cAAc;","names":["import_react_native","import_jsx_runtime","RNText","import_jsx_runtime","React"]}
|
package/native/index.mjs
CHANGED
|
@@ -209,6 +209,8 @@ var Text = ({
|
|
|
209
209
|
numberOfLines,
|
|
210
210
|
id,
|
|
211
211
|
role,
|
|
212
|
+
testID,
|
|
213
|
+
"data-testid": dataTestId,
|
|
212
214
|
style: styleProp,
|
|
213
215
|
...props
|
|
214
216
|
}) => {
|
|
@@ -238,7 +240,7 @@ var Text = ({
|
|
|
238
240
|
{
|
|
239
241
|
style: baseStyle,
|
|
240
242
|
numberOfLines,
|
|
241
|
-
testID: id,
|
|
243
|
+
testID: dataTestId || testID || id,
|
|
242
244
|
accessibilityRole,
|
|
243
245
|
children
|
|
244
246
|
}
|
|
@@ -272,6 +274,7 @@ var ImageUploader = forwardRef(
|
|
|
272
274
|
onDelete,
|
|
273
275
|
disabled = false,
|
|
274
276
|
loading = false,
|
|
277
|
+
testID,
|
|
275
278
|
themeMode,
|
|
276
279
|
themeProductContext
|
|
277
280
|
}, ref) => {
|
|
@@ -586,7 +589,7 @@ var ImageUploader = forwardRef(
|
|
|
586
589
|
return /* @__PURE__ */ jsxs(
|
|
587
590
|
Box,
|
|
588
591
|
{
|
|
589
|
-
|
|
592
|
+
testID: testID || "image-uploader",
|
|
590
593
|
flexDirection: "column",
|
|
591
594
|
gap: rootGap,
|
|
592
595
|
alignItems: "flex-start",
|