@sparkstudio/storage-ui 1.0.30 → 1.0.31

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/dist/index.cjs CHANGED
@@ -34,6 +34,8 @@ __export(index_exports, {
34
34
  HomeView: () => HomeView,
35
35
  S3: () => S3,
36
36
  SingleFileProcessUploader: () => SingleFileProcessUploader,
37
+ SingleImageUploadEditor: () => SingleImageUploadEditor,
38
+ SingleImageView: () => SingleImageView,
37
39
  SparkStudioStorageSDK: () => SparkStudioStorageSDK,
38
40
  TemporaryFileDTO: () => TemporaryFileDTO,
39
41
  UploadContainer: () => UploadContainer,
@@ -1921,29 +1923,495 @@ var SingleFileProcessUploader = ({
1921
1923
  ] });
1922
1924
  };
1923
1925
 
1924
- // src/views/HomeView.tsx
1926
+ // src/components/SingleImageUploadEditor.tsx
1925
1927
  var import_react13 = require("react");
1926
- var import_authentication_ui = require("@sparkstudio/authentication-ui");
1927
1928
  var import_jsx_runtime11 = require("react/jsx-runtime");
1929
+ function SingleImageUploadEditor(props) {
1930
+ const {
1931
+ containerApiBaseUrl,
1932
+ storageApiBaseUrl,
1933
+ value = null,
1934
+ onChange,
1935
+ height = 280,
1936
+ disabled = false
1937
+ } = props;
1938
+ const [isDragging, setIsDragging] = (0, import_react13.useState)(false);
1939
+ const [error, setError] = (0, import_react13.useState)(null);
1940
+ const pendingContainerRef = (0, import_react13.useRef)(null);
1941
+ const [isDeleting, setIsDeleting] = (0, import_react13.useState)(false);
1942
+ const [isLoadingImage, setIsLoadingImage] = (0, import_react13.useState)(false);
1943
+ const [currentImage, setCurrentImage] = (0, import_react13.useState)(null);
1944
+ const sdkDb = (0, import_react13.useMemo)(
1945
+ () => new SparkStudioStorageSDK(containerApiBaseUrl),
1946
+ [containerApiBaseUrl]
1947
+ );
1948
+ const sdkS3 = (0, import_react13.useMemo)(
1949
+ () => new SparkStudioStorageSDK(storageApiBaseUrl),
1950
+ [storageApiBaseUrl]
1951
+ );
1952
+ const busy = disabled || isDeleting || isLoadingImage;
1953
+ (0, import_react13.useEffect)(() => {
1954
+ let cancelled = false;
1955
+ async function loadImage() {
1956
+ if (!value) {
1957
+ setCurrentImage(null);
1958
+ return;
1959
+ }
1960
+ try {
1961
+ setIsLoadingImage(true);
1962
+ const image = await sdkDb.container.Read(value);
1963
+ if (!cancelled) {
1964
+ setCurrentImage(image ?? null);
1965
+ }
1966
+ } catch (err) {
1967
+ if (!cancelled) {
1968
+ setCurrentImage(null);
1969
+ setError(err instanceof Error ? err.message : "Failed to load image.");
1970
+ }
1971
+ } finally {
1972
+ if (!cancelled) {
1973
+ setIsLoadingImage(false);
1974
+ }
1975
+ }
1976
+ }
1977
+ setError(null);
1978
+ loadImage();
1979
+ return () => {
1980
+ cancelled = true;
1981
+ };
1982
+ }, [value, sdkDb]);
1983
+ function getErrorMessage(err, fallback) {
1984
+ return err instanceof Error ? err.message : fallback;
1985
+ }
1986
+ function isImageFile(file) {
1987
+ return file.type.startsWith("image/");
1988
+ }
1989
+ async function createContainerForFile(file) {
1990
+ const contentType = file.type || "application/octet-stream";
1991
+ return sdkDb.container.CreateFileContainer(
1992
+ file.name,
1993
+ file.size,
1994
+ encodeURIComponent(contentType)
1995
+ );
1996
+ }
1997
+ async function getPresignedUrl(file) {
1998
+ const container = await createContainerForFile(file);
1999
+ pendingContainerRef.current = container;
2000
+ let lastError;
2001
+ for (let i = 1; i <= 3; i++) {
2002
+ try {
2003
+ return await sdkS3.s3.GetPreSignedUrl(container);
2004
+ } catch (e) {
2005
+ lastError = e;
2006
+ if (i < 3) {
2007
+ await new Promise((r) => setTimeout(r, 500 * i));
2008
+ }
2009
+ }
2010
+ }
2011
+ throw lastError instanceof Error ? lastError : new Error("Failed to fetch presigned URL");
2012
+ }
2013
+ async function deleteContainerFile(fileId) {
2014
+ const fileContainer = await sdkDb.container.Read(fileId);
2015
+ await sdkDb.container.DeleteContainer(fileId);
2016
+ await sdkS3.s3.DeleteS3(fileContainer);
2017
+ }
2018
+ const { uploads, startUploadsIfNeeded } = UseUploadManager({
2019
+ autoUpload: true,
2020
+ getPresignedUrl,
2021
+ onUploadComplete: async () => {
2022
+ setError(null);
2023
+ const uploaded = pendingContainerRef.current;
2024
+ if (uploaded) {
2025
+ setCurrentImage(uploaded);
2026
+ onChange?.(uploaded.Id);
2027
+ pendingContainerRef.current = null;
2028
+ }
2029
+ },
2030
+ onUploadError: async (_file, err) => {
2031
+ const pending = pendingContainerRef.current;
2032
+ if (pending) {
2033
+ try {
2034
+ await sdkDb.container.DeleteContainer(pending.Id);
2035
+ } catch {
2036
+ }
2037
+ }
2038
+ pendingContainerRef.current = null;
2039
+ setError(getErrorMessage(err, "Upload failed"));
2040
+ }
2041
+ });
2042
+ const visibleUploads = uploads.filter((u) => u.status !== "success");
2043
+ const activeUpload = visibleUploads[0];
2044
+ const showBottomProgress = !!activeUpload;
2045
+ async function replaceWithFile(file) {
2046
+ if (busy) {
2047
+ return;
2048
+ }
2049
+ if (!isImageFile(file)) {
2050
+ setError("Please select an image file.");
2051
+ return;
2052
+ }
2053
+ try {
2054
+ setError(null);
2055
+ if (value) {
2056
+ setIsDeleting(true);
2057
+ await deleteContainerFile(value);
2058
+ setCurrentImage(null);
2059
+ onChange?.(null);
2060
+ }
2061
+ const dt = new DataTransfer();
2062
+ dt.items.add(file);
2063
+ startUploadsIfNeeded(dt.files);
2064
+ } catch (err) {
2065
+ setError(getErrorMessage(err, "Failed to replace image."));
2066
+ } finally {
2067
+ setIsDeleting(false);
2068
+ }
2069
+ }
2070
+ function openPicker() {
2071
+ if (busy) {
2072
+ return;
2073
+ }
2074
+ const input = document.createElement("input");
2075
+ input.type = "file";
2076
+ input.accept = "image/*";
2077
+ input.multiple = false;
2078
+ input.onchange = async () => {
2079
+ const file = input.files?.[0];
2080
+ if (!file) {
2081
+ return;
2082
+ }
2083
+ await replaceWithFile(file);
2084
+ };
2085
+ input.click();
2086
+ }
2087
+ async function handleRemove() {
2088
+ if (!value || busy) {
2089
+ return;
2090
+ }
2091
+ try {
2092
+ setError(null);
2093
+ setIsDeleting(true);
2094
+ await deleteContainerFile(value);
2095
+ setCurrentImage(null);
2096
+ onChange?.(null);
2097
+ } catch (err) {
2098
+ setError(getErrorMessage(err, "Failed to delete image."));
2099
+ } finally {
2100
+ setIsDeleting(false);
2101
+ }
2102
+ }
2103
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { style: { display: "grid", gap: 8 }, children: [
2104
+ error ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { style: { fontSize: 12, color: "crimson" }, children: error }) : null,
2105
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
2106
+ "div",
2107
+ {
2108
+ onDragEnter: (e) => {
2109
+ e.preventDefault();
2110
+ e.stopPropagation();
2111
+ if (!busy) {
2112
+ setIsDragging(true);
2113
+ }
2114
+ },
2115
+ onDragOver: (e) => {
2116
+ e.preventDefault();
2117
+ e.stopPropagation();
2118
+ if (!busy) {
2119
+ setIsDragging(true);
2120
+ }
2121
+ },
2122
+ onDragLeave: (e) => {
2123
+ e.preventDefault();
2124
+ e.stopPropagation();
2125
+ setIsDragging(false);
2126
+ },
2127
+ onDrop: async (e) => {
2128
+ e.preventDefault();
2129
+ e.stopPropagation();
2130
+ setIsDragging(false);
2131
+ if (busy) {
2132
+ return;
2133
+ }
2134
+ const file = e.dataTransfer.files?.[0];
2135
+ if (!file) {
2136
+ return;
2137
+ }
2138
+ await replaceWithFile(file);
2139
+ },
2140
+ style: {
2141
+ position: "relative",
2142
+ borderRadius: 14,
2143
+ border: isDragging ? "2px dashed rgba(13,110,253,0.9)" : "2px dashed rgba(0,0,0,0.15)",
2144
+ background: isDragging ? "rgba(13,110,253,0.06)" : "transparent",
2145
+ padding: 12,
2146
+ minHeight: height,
2147
+ overflow: "hidden"
2148
+ },
2149
+ children: [
2150
+ isDragging ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2151
+ "div",
2152
+ {
2153
+ style: {
2154
+ position: "absolute",
2155
+ inset: 0,
2156
+ borderRadius: 14,
2157
+ display: "grid",
2158
+ placeItems: "center",
2159
+ pointerEvents: "none",
2160
+ background: "rgba(13,110,253,0.08)",
2161
+ fontWeight: 800,
2162
+ zIndex: 3
2163
+ },
2164
+ children: "Drop image to replace"
2165
+ }
2166
+ ) : null,
2167
+ currentImage ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
2168
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2169
+ "div",
2170
+ {
2171
+ style: {
2172
+ border: "1px solid rgba(0,0,0,0.08)",
2173
+ borderRadius: 12,
2174
+ overflow: "hidden",
2175
+ background: "rgba(0,0,0,0.03)",
2176
+ minHeight: height
2177
+ },
2178
+ children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2179
+ "img",
2180
+ {
2181
+ src: currentImage.PublicUrl ?? "",
2182
+ alt: currentImage.Name ?? "Uploaded image",
2183
+ style: {
2184
+ display: "block",
2185
+ width: "100%",
2186
+ height,
2187
+ objectFit: "contain",
2188
+ background: "rgba(0,0,0,0.02)"
2189
+ }
2190
+ }
2191
+ )
2192
+ }
2193
+ ),
2194
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
2195
+ "div",
2196
+ {
2197
+ style: {
2198
+ position: "absolute",
2199
+ top: 20,
2200
+ right: 20,
2201
+ display: "flex",
2202
+ gap: 8,
2203
+ zIndex: 2
2204
+ },
2205
+ children: [
2206
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2207
+ "button",
2208
+ {
2209
+ type: "button",
2210
+ onClick: openPicker,
2211
+ disabled: busy,
2212
+ style: {
2213
+ border: "1px solid rgba(0,0,0,0.18)",
2214
+ background: "white",
2215
+ borderRadius: 10,
2216
+ padding: "8px 10px",
2217
+ cursor: busy ? "not-allowed" : "pointer",
2218
+ fontWeight: 600,
2219
+ boxShadow: "0 2px 8px rgba(0,0,0,0.08)"
2220
+ },
2221
+ children: "Browse\u2026"
2222
+ }
2223
+ ),
2224
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2225
+ "button",
2226
+ {
2227
+ type: "button",
2228
+ onClick: handleRemove,
2229
+ disabled: busy,
2230
+ style: {
2231
+ border: "1px solid rgba(220,53,69,0.35)",
2232
+ background: "white",
2233
+ color: "#dc3545",
2234
+ borderRadius: 10,
2235
+ padding: "8px 10px",
2236
+ cursor: busy ? "not-allowed" : "pointer",
2237
+ fontWeight: 600,
2238
+ boxShadow: "0 2px 8px rgba(0,0,0,0.08)"
2239
+ },
2240
+ children: "Remove"
2241
+ }
2242
+ )
2243
+ ]
2244
+ }
2245
+ )
2246
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2247
+ "div",
2248
+ {
2249
+ style: {
2250
+ minHeight: height,
2251
+ borderRadius: 12,
2252
+ border: "1px solid rgba(0,0,0,0.08)",
2253
+ display: "grid",
2254
+ placeItems: "center",
2255
+ background: "rgba(0,0,0,0.02)",
2256
+ textAlign: "center",
2257
+ padding: 24
2258
+ },
2259
+ children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
2260
+ "div",
2261
+ {
2262
+ style: {
2263
+ display: "grid",
2264
+ justifyItems: "center",
2265
+ gap: 10
2266
+ },
2267
+ children: [
2268
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2269
+ "button",
2270
+ {
2271
+ type: "button",
2272
+ onClick: openPicker,
2273
+ disabled: busy,
2274
+ style: {
2275
+ border: "1px solid rgba(0,0,0,0.18)",
2276
+ background: "white",
2277
+ borderRadius: 10,
2278
+ padding: "10px 16px",
2279
+ cursor: busy ? "not-allowed" : "pointer",
2280
+ fontWeight: 600
2281
+ },
2282
+ children: "Browse\u2026"
2283
+ }
2284
+ ),
2285
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { style: { fontSize: 13, opacity: 0.7 }, children: "Drag and drop an image here" })
2286
+ ]
2287
+ }
2288
+ )
2289
+ }
2290
+ ),
2291
+ showBottomProgress ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2292
+ "div",
2293
+ {
2294
+ style: {
2295
+ position: "absolute",
2296
+ left: 0,
2297
+ right: 0,
2298
+ bottom: 0,
2299
+ height: 4,
2300
+ background: "rgba(0,0,0,0.08)",
2301
+ zIndex: 4
2302
+ },
2303
+ title: activeUpload.file.name,
2304
+ children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2305
+ "div",
2306
+ {
2307
+ role: "progressbar",
2308
+ "aria-valuenow": activeUpload.progress,
2309
+ "aria-valuemin": 0,
2310
+ "aria-valuemax": 100,
2311
+ style: {
2312
+ height: "100%",
2313
+ width: `${activeUpload.progress}%`,
2314
+ transition: "width 120ms linear",
2315
+ background: activeUpload.status === "error" ? "#dc3545" : "#0d6efd"
2316
+ }
2317
+ }
2318
+ )
2319
+ }
2320
+ ) : null
2321
+ ]
2322
+ }
2323
+ )
2324
+ ] });
2325
+ }
2326
+
2327
+ // src/components/SingleImageView.tsx
2328
+ var import_react14 = require("react");
2329
+ var import_jsx_runtime12 = require("react/jsx-runtime");
2330
+ function SingleImageView(props) {
2331
+ const { containerApiBaseUrl, value, height = 280, onLoaded } = props;
2332
+ const [imageUrl, setImageUrl] = (0, import_react14.useState)(null);
2333
+ const [loading, setLoading] = (0, import_react14.useState)(false);
2334
+ const sdk = (0, import_react14.useMemo)(
2335
+ () => new SparkStudioStorageSDK(containerApiBaseUrl),
2336
+ [containerApiBaseUrl]
2337
+ );
2338
+ (0, import_react14.useEffect)(() => {
2339
+ if (!value) {
2340
+ setImageUrl(null);
2341
+ onLoaded?.(null);
2342
+ return;
2343
+ }
2344
+ setLoading(true);
2345
+ sdk.container.Read(value).then((x) => {
2346
+ const url = x?.PublicUrl ?? null;
2347
+ setImageUrl(url);
2348
+ onLoaded?.(url);
2349
+ }).catch(() => {
2350
+ setImageUrl(null);
2351
+ onLoaded?.(null);
2352
+ }).finally(() => setLoading(false));
2353
+ }, [sdk, value, onLoaded]);
2354
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
2355
+ "div",
2356
+ {
2357
+ style: {
2358
+ height,
2359
+ width: "100%",
2360
+ overflow: "hidden",
2361
+ display: "grid",
2362
+ placeItems: "center",
2363
+ position: "relative"
2364
+ },
2365
+ children: [
2366
+ imageUrl && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2367
+ "img",
2368
+ {
2369
+ src: imageUrl,
2370
+ alt: "Image",
2371
+ style: {
2372
+ width: "100%",
2373
+ height: "100%",
2374
+ objectFit: "cover",
2375
+ display: "block"
2376
+ }
2377
+ }
2378
+ ),
2379
+ loading && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2380
+ "div",
2381
+ {
2382
+ className: "spinner-border text-primary",
2383
+ style: { position: "absolute" }
2384
+ }
2385
+ ),
2386
+ !loading && !imageUrl && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { style: { fontSize: 13, opacity: 0.6 }, children: "No image" })
2387
+ ]
2388
+ }
2389
+ );
2390
+ }
2391
+
2392
+ // src/views/HomeView.tsx
2393
+ var import_react15 = require("react");
2394
+ var import_authentication_ui = require("@sparkstudio/authentication-ui");
2395
+ var import_jsx_runtime13 = require("react/jsx-runtime");
1928
2396
  var CONTAINER_API = "https://lf9zyufpuk.execute-api.us-east-2.amazonaws.com/Prod";
1929
2397
  var STORAGE_API = "https://iq0gmcn0pd.execute-api.us-east-2.amazonaws.com/Prod";
1930
2398
  function HomeView() {
1931
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2399
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1932
2400
  import_authentication_ui.AuthenticatorProvider,
1933
2401
  {
1934
2402
  googleClientId: import_authentication_ui.AppSettings.GoogleClientId,
1935
2403
  authenticationUrl: import_authentication_ui.AppSettings.AuthenticationUrl,
1936
2404
  accountsUrl: import_authentication_ui.AppSettings.AccountsUrl,
1937
- children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(HomeContent, {})
2405
+ children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(HomeContent, {})
1938
2406
  }
1939
2407
  );
1940
2408
  }
1941
2409
  function HomeContent() {
1942
2410
  const { user } = (0, import_authentication_ui.useUser)();
1943
- const [ids, setIds] = (0, import_react13.useState)([]);
1944
- const [selectedId, setSelectedId] = (0, import_react13.useState)(void 0);
1945
- const [selectedFile, setSelectedFile] = (0, import_react13.useState)(null);
1946
- (0, import_react13.useEffect)(() => {
2411
+ const [ids, setIds] = (0, import_react15.useState)([]);
2412
+ const [selectedId, setSelectedId] = (0, import_react15.useState)(void 0);
2413
+ const [selectedFile, setSelectedFile] = (0, import_react15.useState)(null);
2414
+ (0, import_react15.useEffect)(() => {
1947
2415
  if (selectedId && !ids.includes(selectedId)) {
1948
2416
  setSelectedId(void 0);
1949
2417
  setSelectedFile(null);
@@ -1956,10 +2424,10 @@ function HomeContent() {
1956
2424
  new TemporaryFileDTO({ Name: file.name, ContentType: contentType })
1957
2425
  );
1958
2426
  }
1959
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
1960
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_authentication_ui.UserInfoCard, {}),
1961
- user ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
1962
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2427
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
2428
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_authentication_ui.UserInfoCard, {}),
2429
+ user ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
2430
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1963
2431
  SingleFileProcessUploader,
1964
2432
  {
1965
2433
  uploadOnDrop: true,
@@ -1970,7 +2438,7 @@ function HomeContent() {
1970
2438
  }
1971
2439
  }
1972
2440
  ),
1973
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2441
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1974
2442
  ContainerIdGridPanel,
1975
2443
  {
1976
2444
  containerApiBaseUrl: CONTAINER_API,
@@ -1993,7 +2461,7 @@ function HomeContent() {
1993
2461
  }
1994
2462
  }
1995
2463
  ),
1996
- selectedFile ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
2464
+ selectedFile ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
1997
2465
  "div",
1998
2466
  {
1999
2467
  style: {
@@ -2003,9 +2471,9 @@ function HomeContent() {
2003
2471
  borderRadius: 12
2004
2472
  },
2005
2473
  children: [
2006
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { style: { fontWeight: 700 }, children: "Selected file" }),
2007
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { children: selectedFile.Name ?? "(no name)" }),
2008
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { style: { fontSize: 12, opacity: 0.7 }, children: selectedFile.PublicUrl ?? "(no public url)" })
2474
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: { fontWeight: 700 }, children: "Selected file" }),
2475
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { children: selectedFile.Name ?? "(no name)" }),
2476
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: { fontSize: 12, opacity: 0.7 }, children: selectedFile.PublicUrl ?? "(no public url)" })
2009
2477
  ]
2010
2478
  }
2011
2479
  ) : null
@@ -2028,6 +2496,8 @@ function HomeContent() {
2028
2496
  HomeView,
2029
2497
  S3,
2030
2498
  SingleFileProcessUploader,
2499
+ SingleImageUploadEditor,
2500
+ SingleImageView,
2031
2501
  SparkStudioStorageSDK,
2032
2502
  TemporaryFileDTO,
2033
2503
  UploadContainer,
package/dist/index.d.cts CHANGED
@@ -1,6 +1,7 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import * as React from 'react';
3
3
  import React__default from 'react';
4
+ import { Guid } from '@sparkstudio/authentication-ui';
4
5
 
5
6
  declare enum ContainerType {
6
7
  File = 0,
@@ -230,6 +231,22 @@ interface SingleFileProcessUploaderProps {
230
231
  }
231
232
  declare const SingleFileProcessUploader: React__default.FC<SingleFileProcessUploaderProps>;
232
233
 
234
+ declare function SingleImageUploadEditor(props: {
235
+ containerApiBaseUrl: string;
236
+ storageApiBaseUrl: string;
237
+ value?: Guid | null;
238
+ onChange?: (fileId: Guid | null) => void;
239
+ height?: number;
240
+ disabled?: boolean;
241
+ }): react_jsx_runtime.JSX.Element;
242
+
243
+ declare function SingleImageView(props: {
244
+ containerApiBaseUrl: string;
245
+ value: Guid | null;
246
+ height?: number;
247
+ onLoaded?: (url: string | null) => void;
248
+ }): react_jsx_runtime.JSX.Element;
249
+
233
250
  interface UploadDropzoneProps {
234
251
  isDragging: boolean;
235
252
  onDragOver?: (e: React__default.DragEvent<HTMLDivElement>) => void;
@@ -288,4 +305,4 @@ declare function UseUploadManager({ autoUpload, getPresignedUrl, onUploadComplet
288
305
 
289
306
  declare function HomeView(): react_jsx_runtime.JSX.Element;
290
307
 
291
- export { AWSPresignedUrlDTO, Container, ContainerDTO, ContainerIdGridPanel, ContainerType, ContainerUploadPanel, DesktopFileIcon, type DesktopFileIconProps, FileGridUploadPanel, FileIconCard, type FileIconCardProps, FileIconGrid, Home, HomeView, type IAWSPresignedUrlDTO, type IContainerDTO, type ITemporaryFileDTO, S3, SingleFileProcessUploader, type SingleFileProcessUploaderProps, SparkStudioStorageSDK, TemporaryFileDTO, UploadContainer, type UploadContainerHandle, type UploadContainerProps, UploadDropzone, UploadFileToS3, UploadProgressList, type UploadState, type UploadStatus, UseContainers, UseUploadManager };
308
+ export { AWSPresignedUrlDTO, Container, ContainerDTO, ContainerIdGridPanel, ContainerType, ContainerUploadPanel, DesktopFileIcon, type DesktopFileIconProps, FileGridUploadPanel, FileIconCard, type FileIconCardProps, FileIconGrid, Home, HomeView, type IAWSPresignedUrlDTO, type IContainerDTO, type ITemporaryFileDTO, S3, SingleFileProcessUploader, type SingleFileProcessUploaderProps, SingleImageUploadEditor, SingleImageView, SparkStudioStorageSDK, TemporaryFileDTO, UploadContainer, type UploadContainerHandle, type UploadContainerProps, UploadDropzone, UploadFileToS3, UploadProgressList, type UploadState, type UploadStatus, UseContainers, UseUploadManager };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import * as React from 'react';
3
3
  import React__default from 'react';
4
+ import { Guid } from '@sparkstudio/authentication-ui';
4
5
 
5
6
  declare enum ContainerType {
6
7
  File = 0,
@@ -230,6 +231,22 @@ interface SingleFileProcessUploaderProps {
230
231
  }
231
232
  declare const SingleFileProcessUploader: React__default.FC<SingleFileProcessUploaderProps>;
232
233
 
234
+ declare function SingleImageUploadEditor(props: {
235
+ containerApiBaseUrl: string;
236
+ storageApiBaseUrl: string;
237
+ value?: Guid | null;
238
+ onChange?: (fileId: Guid | null) => void;
239
+ height?: number;
240
+ disabled?: boolean;
241
+ }): react_jsx_runtime.JSX.Element;
242
+
243
+ declare function SingleImageView(props: {
244
+ containerApiBaseUrl: string;
245
+ value: Guid | null;
246
+ height?: number;
247
+ onLoaded?: (url: string | null) => void;
248
+ }): react_jsx_runtime.JSX.Element;
249
+
233
250
  interface UploadDropzoneProps {
234
251
  isDragging: boolean;
235
252
  onDragOver?: (e: React__default.DragEvent<HTMLDivElement>) => void;
@@ -288,4 +305,4 @@ declare function UseUploadManager({ autoUpload, getPresignedUrl, onUploadComplet
288
305
 
289
306
  declare function HomeView(): react_jsx_runtime.JSX.Element;
290
307
 
291
- export { AWSPresignedUrlDTO, Container, ContainerDTO, ContainerIdGridPanel, ContainerType, ContainerUploadPanel, DesktopFileIcon, type DesktopFileIconProps, FileGridUploadPanel, FileIconCard, type FileIconCardProps, FileIconGrid, Home, HomeView, type IAWSPresignedUrlDTO, type IContainerDTO, type ITemporaryFileDTO, S3, SingleFileProcessUploader, type SingleFileProcessUploaderProps, SparkStudioStorageSDK, TemporaryFileDTO, UploadContainer, type UploadContainerHandle, type UploadContainerProps, UploadDropzone, UploadFileToS3, UploadProgressList, type UploadState, type UploadStatus, UseContainers, UseUploadManager };
308
+ export { AWSPresignedUrlDTO, Container, ContainerDTO, ContainerIdGridPanel, ContainerType, ContainerUploadPanel, DesktopFileIcon, type DesktopFileIconProps, FileGridUploadPanel, FileIconCard, type FileIconCardProps, FileIconGrid, Home, HomeView, type IAWSPresignedUrlDTO, type IContainerDTO, type ITemporaryFileDTO, S3, SingleFileProcessUploader, type SingleFileProcessUploaderProps, SingleImageUploadEditor, SingleImageView, SparkStudioStorageSDK, TemporaryFileDTO, UploadContainer, type UploadContainerHandle, type UploadContainerProps, UploadDropzone, UploadFileToS3, UploadProgressList, type UploadState, type UploadStatus, UseContainers, UseUploadManager };
package/dist/index.js CHANGED
@@ -1890,34 +1890,500 @@ var SingleFileProcessUploader = ({
1890
1890
  ] });
1891
1891
  };
1892
1892
 
1893
+ // src/components/SingleImageUploadEditor.tsx
1894
+ import { useEffect as useEffect4, useMemo as useMemo5, useRef as useRef5, useState as useState9 } from "react";
1895
+ import { Fragment as Fragment3, jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
1896
+ function SingleImageUploadEditor(props) {
1897
+ const {
1898
+ containerApiBaseUrl,
1899
+ storageApiBaseUrl,
1900
+ value = null,
1901
+ onChange,
1902
+ height = 280,
1903
+ disabled = false
1904
+ } = props;
1905
+ const [isDragging, setIsDragging] = useState9(false);
1906
+ const [error, setError] = useState9(null);
1907
+ const pendingContainerRef = useRef5(null);
1908
+ const [isDeleting, setIsDeleting] = useState9(false);
1909
+ const [isLoadingImage, setIsLoadingImage] = useState9(false);
1910
+ const [currentImage, setCurrentImage] = useState9(null);
1911
+ const sdkDb = useMemo5(
1912
+ () => new SparkStudioStorageSDK(containerApiBaseUrl),
1913
+ [containerApiBaseUrl]
1914
+ );
1915
+ const sdkS3 = useMemo5(
1916
+ () => new SparkStudioStorageSDK(storageApiBaseUrl),
1917
+ [storageApiBaseUrl]
1918
+ );
1919
+ const busy = disabled || isDeleting || isLoadingImage;
1920
+ useEffect4(() => {
1921
+ let cancelled = false;
1922
+ async function loadImage() {
1923
+ if (!value) {
1924
+ setCurrentImage(null);
1925
+ return;
1926
+ }
1927
+ try {
1928
+ setIsLoadingImage(true);
1929
+ const image = await sdkDb.container.Read(value);
1930
+ if (!cancelled) {
1931
+ setCurrentImage(image ?? null);
1932
+ }
1933
+ } catch (err) {
1934
+ if (!cancelled) {
1935
+ setCurrentImage(null);
1936
+ setError(err instanceof Error ? err.message : "Failed to load image.");
1937
+ }
1938
+ } finally {
1939
+ if (!cancelled) {
1940
+ setIsLoadingImage(false);
1941
+ }
1942
+ }
1943
+ }
1944
+ setError(null);
1945
+ loadImage();
1946
+ return () => {
1947
+ cancelled = true;
1948
+ };
1949
+ }, [value, sdkDb]);
1950
+ function getErrorMessage(err, fallback) {
1951
+ return err instanceof Error ? err.message : fallback;
1952
+ }
1953
+ function isImageFile(file) {
1954
+ return file.type.startsWith("image/");
1955
+ }
1956
+ async function createContainerForFile(file) {
1957
+ const contentType = file.type || "application/octet-stream";
1958
+ return sdkDb.container.CreateFileContainer(
1959
+ file.name,
1960
+ file.size,
1961
+ encodeURIComponent(contentType)
1962
+ );
1963
+ }
1964
+ async function getPresignedUrl(file) {
1965
+ const container = await createContainerForFile(file);
1966
+ pendingContainerRef.current = container;
1967
+ let lastError;
1968
+ for (let i = 1; i <= 3; i++) {
1969
+ try {
1970
+ return await sdkS3.s3.GetPreSignedUrl(container);
1971
+ } catch (e) {
1972
+ lastError = e;
1973
+ if (i < 3) {
1974
+ await new Promise((r) => setTimeout(r, 500 * i));
1975
+ }
1976
+ }
1977
+ }
1978
+ throw lastError instanceof Error ? lastError : new Error("Failed to fetch presigned URL");
1979
+ }
1980
+ async function deleteContainerFile(fileId) {
1981
+ const fileContainer = await sdkDb.container.Read(fileId);
1982
+ await sdkDb.container.DeleteContainer(fileId);
1983
+ await sdkS3.s3.DeleteS3(fileContainer);
1984
+ }
1985
+ const { uploads, startUploadsIfNeeded } = UseUploadManager({
1986
+ autoUpload: true,
1987
+ getPresignedUrl,
1988
+ onUploadComplete: async () => {
1989
+ setError(null);
1990
+ const uploaded = pendingContainerRef.current;
1991
+ if (uploaded) {
1992
+ setCurrentImage(uploaded);
1993
+ onChange?.(uploaded.Id);
1994
+ pendingContainerRef.current = null;
1995
+ }
1996
+ },
1997
+ onUploadError: async (_file, err) => {
1998
+ const pending = pendingContainerRef.current;
1999
+ if (pending) {
2000
+ try {
2001
+ await sdkDb.container.DeleteContainer(pending.Id);
2002
+ } catch {
2003
+ }
2004
+ }
2005
+ pendingContainerRef.current = null;
2006
+ setError(getErrorMessage(err, "Upload failed"));
2007
+ }
2008
+ });
2009
+ const visibleUploads = uploads.filter((u) => u.status !== "success");
2010
+ const activeUpload = visibleUploads[0];
2011
+ const showBottomProgress = !!activeUpload;
2012
+ async function replaceWithFile(file) {
2013
+ if (busy) {
2014
+ return;
2015
+ }
2016
+ if (!isImageFile(file)) {
2017
+ setError("Please select an image file.");
2018
+ return;
2019
+ }
2020
+ try {
2021
+ setError(null);
2022
+ if (value) {
2023
+ setIsDeleting(true);
2024
+ await deleteContainerFile(value);
2025
+ setCurrentImage(null);
2026
+ onChange?.(null);
2027
+ }
2028
+ const dt = new DataTransfer();
2029
+ dt.items.add(file);
2030
+ startUploadsIfNeeded(dt.files);
2031
+ } catch (err) {
2032
+ setError(getErrorMessage(err, "Failed to replace image."));
2033
+ } finally {
2034
+ setIsDeleting(false);
2035
+ }
2036
+ }
2037
+ function openPicker() {
2038
+ if (busy) {
2039
+ return;
2040
+ }
2041
+ const input = document.createElement("input");
2042
+ input.type = "file";
2043
+ input.accept = "image/*";
2044
+ input.multiple = false;
2045
+ input.onchange = async () => {
2046
+ const file = input.files?.[0];
2047
+ if (!file) {
2048
+ return;
2049
+ }
2050
+ await replaceWithFile(file);
2051
+ };
2052
+ input.click();
2053
+ }
2054
+ async function handleRemove() {
2055
+ if (!value || busy) {
2056
+ return;
2057
+ }
2058
+ try {
2059
+ setError(null);
2060
+ setIsDeleting(true);
2061
+ await deleteContainerFile(value);
2062
+ setCurrentImage(null);
2063
+ onChange?.(null);
2064
+ } catch (err) {
2065
+ setError(getErrorMessage(err, "Failed to delete image."));
2066
+ } finally {
2067
+ setIsDeleting(false);
2068
+ }
2069
+ }
2070
+ return /* @__PURE__ */ jsxs8("div", { style: { display: "grid", gap: 8 }, children: [
2071
+ error ? /* @__PURE__ */ jsx11("div", { style: { fontSize: 12, color: "crimson" }, children: error }) : null,
2072
+ /* @__PURE__ */ jsxs8(
2073
+ "div",
2074
+ {
2075
+ onDragEnter: (e) => {
2076
+ e.preventDefault();
2077
+ e.stopPropagation();
2078
+ if (!busy) {
2079
+ setIsDragging(true);
2080
+ }
2081
+ },
2082
+ onDragOver: (e) => {
2083
+ e.preventDefault();
2084
+ e.stopPropagation();
2085
+ if (!busy) {
2086
+ setIsDragging(true);
2087
+ }
2088
+ },
2089
+ onDragLeave: (e) => {
2090
+ e.preventDefault();
2091
+ e.stopPropagation();
2092
+ setIsDragging(false);
2093
+ },
2094
+ onDrop: async (e) => {
2095
+ e.preventDefault();
2096
+ e.stopPropagation();
2097
+ setIsDragging(false);
2098
+ if (busy) {
2099
+ return;
2100
+ }
2101
+ const file = e.dataTransfer.files?.[0];
2102
+ if (!file) {
2103
+ return;
2104
+ }
2105
+ await replaceWithFile(file);
2106
+ },
2107
+ style: {
2108
+ position: "relative",
2109
+ borderRadius: 14,
2110
+ border: isDragging ? "2px dashed rgba(13,110,253,0.9)" : "2px dashed rgba(0,0,0,0.15)",
2111
+ background: isDragging ? "rgba(13,110,253,0.06)" : "transparent",
2112
+ padding: 12,
2113
+ minHeight: height,
2114
+ overflow: "hidden"
2115
+ },
2116
+ children: [
2117
+ isDragging ? /* @__PURE__ */ jsx11(
2118
+ "div",
2119
+ {
2120
+ style: {
2121
+ position: "absolute",
2122
+ inset: 0,
2123
+ borderRadius: 14,
2124
+ display: "grid",
2125
+ placeItems: "center",
2126
+ pointerEvents: "none",
2127
+ background: "rgba(13,110,253,0.08)",
2128
+ fontWeight: 800,
2129
+ zIndex: 3
2130
+ },
2131
+ children: "Drop image to replace"
2132
+ }
2133
+ ) : null,
2134
+ currentImage ? /* @__PURE__ */ jsxs8(Fragment3, { children: [
2135
+ /* @__PURE__ */ jsx11(
2136
+ "div",
2137
+ {
2138
+ style: {
2139
+ border: "1px solid rgba(0,0,0,0.08)",
2140
+ borderRadius: 12,
2141
+ overflow: "hidden",
2142
+ background: "rgba(0,0,0,0.03)",
2143
+ minHeight: height
2144
+ },
2145
+ children: /* @__PURE__ */ jsx11(
2146
+ "img",
2147
+ {
2148
+ src: currentImage.PublicUrl ?? "",
2149
+ alt: currentImage.Name ?? "Uploaded image",
2150
+ style: {
2151
+ display: "block",
2152
+ width: "100%",
2153
+ height,
2154
+ objectFit: "contain",
2155
+ background: "rgba(0,0,0,0.02)"
2156
+ }
2157
+ }
2158
+ )
2159
+ }
2160
+ ),
2161
+ /* @__PURE__ */ jsxs8(
2162
+ "div",
2163
+ {
2164
+ style: {
2165
+ position: "absolute",
2166
+ top: 20,
2167
+ right: 20,
2168
+ display: "flex",
2169
+ gap: 8,
2170
+ zIndex: 2
2171
+ },
2172
+ children: [
2173
+ /* @__PURE__ */ jsx11(
2174
+ "button",
2175
+ {
2176
+ type: "button",
2177
+ onClick: openPicker,
2178
+ disabled: busy,
2179
+ style: {
2180
+ border: "1px solid rgba(0,0,0,0.18)",
2181
+ background: "white",
2182
+ borderRadius: 10,
2183
+ padding: "8px 10px",
2184
+ cursor: busy ? "not-allowed" : "pointer",
2185
+ fontWeight: 600,
2186
+ boxShadow: "0 2px 8px rgba(0,0,0,0.08)"
2187
+ },
2188
+ children: "Browse\u2026"
2189
+ }
2190
+ ),
2191
+ /* @__PURE__ */ jsx11(
2192
+ "button",
2193
+ {
2194
+ type: "button",
2195
+ onClick: handleRemove,
2196
+ disabled: busy,
2197
+ style: {
2198
+ border: "1px solid rgba(220,53,69,0.35)",
2199
+ background: "white",
2200
+ color: "#dc3545",
2201
+ borderRadius: 10,
2202
+ padding: "8px 10px",
2203
+ cursor: busy ? "not-allowed" : "pointer",
2204
+ fontWeight: 600,
2205
+ boxShadow: "0 2px 8px rgba(0,0,0,0.08)"
2206
+ },
2207
+ children: "Remove"
2208
+ }
2209
+ )
2210
+ ]
2211
+ }
2212
+ )
2213
+ ] }) : /* @__PURE__ */ jsx11(
2214
+ "div",
2215
+ {
2216
+ style: {
2217
+ minHeight: height,
2218
+ borderRadius: 12,
2219
+ border: "1px solid rgba(0,0,0,0.08)",
2220
+ display: "grid",
2221
+ placeItems: "center",
2222
+ background: "rgba(0,0,0,0.02)",
2223
+ textAlign: "center",
2224
+ padding: 24
2225
+ },
2226
+ children: /* @__PURE__ */ jsxs8(
2227
+ "div",
2228
+ {
2229
+ style: {
2230
+ display: "grid",
2231
+ justifyItems: "center",
2232
+ gap: 10
2233
+ },
2234
+ children: [
2235
+ /* @__PURE__ */ jsx11(
2236
+ "button",
2237
+ {
2238
+ type: "button",
2239
+ onClick: openPicker,
2240
+ disabled: busy,
2241
+ style: {
2242
+ border: "1px solid rgba(0,0,0,0.18)",
2243
+ background: "white",
2244
+ borderRadius: 10,
2245
+ padding: "10px 16px",
2246
+ cursor: busy ? "not-allowed" : "pointer",
2247
+ fontWeight: 600
2248
+ },
2249
+ children: "Browse\u2026"
2250
+ }
2251
+ ),
2252
+ /* @__PURE__ */ jsx11("div", { style: { fontSize: 13, opacity: 0.7 }, children: "Drag and drop an image here" })
2253
+ ]
2254
+ }
2255
+ )
2256
+ }
2257
+ ),
2258
+ showBottomProgress ? /* @__PURE__ */ jsx11(
2259
+ "div",
2260
+ {
2261
+ style: {
2262
+ position: "absolute",
2263
+ left: 0,
2264
+ right: 0,
2265
+ bottom: 0,
2266
+ height: 4,
2267
+ background: "rgba(0,0,0,0.08)",
2268
+ zIndex: 4
2269
+ },
2270
+ title: activeUpload.file.name,
2271
+ children: /* @__PURE__ */ jsx11(
2272
+ "div",
2273
+ {
2274
+ role: "progressbar",
2275
+ "aria-valuenow": activeUpload.progress,
2276
+ "aria-valuemin": 0,
2277
+ "aria-valuemax": 100,
2278
+ style: {
2279
+ height: "100%",
2280
+ width: `${activeUpload.progress}%`,
2281
+ transition: "width 120ms linear",
2282
+ background: activeUpload.status === "error" ? "#dc3545" : "#0d6efd"
2283
+ }
2284
+ }
2285
+ )
2286
+ }
2287
+ ) : null
2288
+ ]
2289
+ }
2290
+ )
2291
+ ] });
2292
+ }
2293
+
2294
+ // src/components/SingleImageView.tsx
2295
+ import { useEffect as useEffect5, useMemo as useMemo6, useState as useState10 } from "react";
2296
+ import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
2297
+ function SingleImageView(props) {
2298
+ const { containerApiBaseUrl, value, height = 280, onLoaded } = props;
2299
+ const [imageUrl, setImageUrl] = useState10(null);
2300
+ const [loading, setLoading] = useState10(false);
2301
+ const sdk = useMemo6(
2302
+ () => new SparkStudioStorageSDK(containerApiBaseUrl),
2303
+ [containerApiBaseUrl]
2304
+ );
2305
+ useEffect5(() => {
2306
+ if (!value) {
2307
+ setImageUrl(null);
2308
+ onLoaded?.(null);
2309
+ return;
2310
+ }
2311
+ setLoading(true);
2312
+ sdk.container.Read(value).then((x) => {
2313
+ const url = x?.PublicUrl ?? null;
2314
+ setImageUrl(url);
2315
+ onLoaded?.(url);
2316
+ }).catch(() => {
2317
+ setImageUrl(null);
2318
+ onLoaded?.(null);
2319
+ }).finally(() => setLoading(false));
2320
+ }, [sdk, value, onLoaded]);
2321
+ return /* @__PURE__ */ jsxs9(
2322
+ "div",
2323
+ {
2324
+ style: {
2325
+ height,
2326
+ width: "100%",
2327
+ overflow: "hidden",
2328
+ display: "grid",
2329
+ placeItems: "center",
2330
+ position: "relative"
2331
+ },
2332
+ children: [
2333
+ imageUrl && /* @__PURE__ */ jsx12(
2334
+ "img",
2335
+ {
2336
+ src: imageUrl,
2337
+ alt: "Image",
2338
+ style: {
2339
+ width: "100%",
2340
+ height: "100%",
2341
+ objectFit: "cover",
2342
+ display: "block"
2343
+ }
2344
+ }
2345
+ ),
2346
+ loading && /* @__PURE__ */ jsx12(
2347
+ "div",
2348
+ {
2349
+ className: "spinner-border text-primary",
2350
+ style: { position: "absolute" }
2351
+ }
2352
+ ),
2353
+ !loading && !imageUrl && /* @__PURE__ */ jsx12("div", { style: { fontSize: 13, opacity: 0.6 }, children: "No image" })
2354
+ ]
2355
+ }
2356
+ );
2357
+ }
2358
+
1893
2359
  // src/views/HomeView.tsx
1894
- import { useEffect as useEffect4, useState as useState9 } from "react";
2360
+ import { useEffect as useEffect6, useState as useState11 } from "react";
1895
2361
  import {
1896
2362
  AppSettings,
1897
2363
  AuthenticatorProvider,
1898
2364
  UserInfoCard,
1899
2365
  useUser
1900
2366
  } from "@sparkstudio/authentication-ui";
1901
- import { Fragment as Fragment3, jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
2367
+ import { Fragment as Fragment4, jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
1902
2368
  var CONTAINER_API = "https://lf9zyufpuk.execute-api.us-east-2.amazonaws.com/Prod";
1903
2369
  var STORAGE_API = "https://iq0gmcn0pd.execute-api.us-east-2.amazonaws.com/Prod";
1904
2370
  function HomeView() {
1905
- return /* @__PURE__ */ jsx11(
2371
+ return /* @__PURE__ */ jsx13(
1906
2372
  AuthenticatorProvider,
1907
2373
  {
1908
2374
  googleClientId: AppSettings.GoogleClientId,
1909
2375
  authenticationUrl: AppSettings.AuthenticationUrl,
1910
2376
  accountsUrl: AppSettings.AccountsUrl,
1911
- children: /* @__PURE__ */ jsx11(HomeContent, {})
2377
+ children: /* @__PURE__ */ jsx13(HomeContent, {})
1912
2378
  }
1913
2379
  );
1914
2380
  }
1915
2381
  function HomeContent() {
1916
2382
  const { user } = useUser();
1917
- const [ids, setIds] = useState9([]);
1918
- const [selectedId, setSelectedId] = useState9(void 0);
1919
- const [selectedFile, setSelectedFile] = useState9(null);
1920
- useEffect4(() => {
2383
+ const [ids, setIds] = useState11([]);
2384
+ const [selectedId, setSelectedId] = useState11(void 0);
2385
+ const [selectedFile, setSelectedFile] = useState11(null);
2386
+ useEffect6(() => {
1921
2387
  if (selectedId && !ids.includes(selectedId)) {
1922
2388
  setSelectedId(void 0);
1923
2389
  setSelectedFile(null);
@@ -1930,10 +2396,10 @@ function HomeContent() {
1930
2396
  new TemporaryFileDTO({ Name: file.name, ContentType: contentType })
1931
2397
  );
1932
2398
  }
1933
- return /* @__PURE__ */ jsxs8(Fragment3, { children: [
1934
- /* @__PURE__ */ jsx11(UserInfoCard, {}),
1935
- user ? /* @__PURE__ */ jsxs8(Fragment3, { children: [
1936
- /* @__PURE__ */ jsx11(
2399
+ return /* @__PURE__ */ jsxs10(Fragment4, { children: [
2400
+ /* @__PURE__ */ jsx13(UserInfoCard, {}),
2401
+ user ? /* @__PURE__ */ jsxs10(Fragment4, { children: [
2402
+ /* @__PURE__ */ jsx13(
1937
2403
  SingleFileProcessUploader,
1938
2404
  {
1939
2405
  uploadOnDrop: true,
@@ -1944,7 +2410,7 @@ function HomeContent() {
1944
2410
  }
1945
2411
  }
1946
2412
  ),
1947
- /* @__PURE__ */ jsx11(
2413
+ /* @__PURE__ */ jsx13(
1948
2414
  ContainerIdGridPanel,
1949
2415
  {
1950
2416
  containerApiBaseUrl: CONTAINER_API,
@@ -1967,7 +2433,7 @@ function HomeContent() {
1967
2433
  }
1968
2434
  }
1969
2435
  ),
1970
- selectedFile ? /* @__PURE__ */ jsxs8(
2436
+ selectedFile ? /* @__PURE__ */ jsxs10(
1971
2437
  "div",
1972
2438
  {
1973
2439
  style: {
@@ -1977,9 +2443,9 @@ function HomeContent() {
1977
2443
  borderRadius: 12
1978
2444
  },
1979
2445
  children: [
1980
- /* @__PURE__ */ jsx11("div", { style: { fontWeight: 700 }, children: "Selected file" }),
1981
- /* @__PURE__ */ jsx11("div", { children: selectedFile.Name ?? "(no name)" }),
1982
- /* @__PURE__ */ jsx11("div", { style: { fontSize: 12, opacity: 0.7 }, children: selectedFile.PublicUrl ?? "(no public url)" })
2446
+ /* @__PURE__ */ jsx13("div", { style: { fontWeight: 700 }, children: "Selected file" }),
2447
+ /* @__PURE__ */ jsx13("div", { children: selectedFile.Name ?? "(no name)" }),
2448
+ /* @__PURE__ */ jsx13("div", { style: { fontSize: 12, opacity: 0.7 }, children: selectedFile.PublicUrl ?? "(no public url)" })
1983
2449
  ]
1984
2450
  }
1985
2451
  ) : null
@@ -2001,6 +2467,8 @@ export {
2001
2467
  HomeView,
2002
2468
  S3,
2003
2469
  SingleFileProcessUploader,
2470
+ SingleImageUploadEditor,
2471
+ SingleImageView,
2004
2472
  SparkStudioStorageSDK,
2005
2473
  TemporaryFileDTO,
2006
2474
  UploadContainer,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sparkstudio/storage-ui",
3
- "version": "1.0.30",
3
+ "version": "1.0.31",
4
4
  "type": "module",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.js",