@dust-tt/sparkle 0.2.490 → 0.2.491

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/dist/cjs/index.js +1 -1
  2. package/dist/esm/components/Breadcrumbs.d.ts +2 -1
  3. package/dist/esm/components/Breadcrumbs.d.ts.map +1 -1
  4. package/dist/esm/components/Breadcrumbs.js +14 -14
  5. package/dist/esm/components/Breadcrumbs.js.map +1 -1
  6. package/dist/esm/components/InteractiveImageGrid.d.ts +14 -0
  7. package/dist/esm/components/InteractiveImageGrid.d.ts.map +1 -0
  8. package/dist/esm/components/InteractiveImageGrid.js +137 -0
  9. package/dist/esm/components/InteractiveImageGrid.js.map +1 -0
  10. package/dist/esm/components/index.d.ts +1 -1
  11. package/dist/esm/components/index.d.ts.map +1 -1
  12. package/dist/esm/components/index.js +1 -1
  13. package/dist/esm/components/index.js.map +1 -1
  14. package/dist/esm/stories/Breadcrumbs.stories.d.ts +11 -1
  15. package/dist/esm/stories/Breadcrumbs.stories.d.ts.map +1 -1
  16. package/dist/esm/stories/Breadcrumbs.stories.js +14 -6
  17. package/dist/esm/stories/Breadcrumbs.stories.js.map +1 -1
  18. package/dist/esm/stories/{InteractiveImage.stories.d.ts → InteractiveImageGrid.stories.d.ts} +1 -1
  19. package/dist/esm/stories/InteractiveImageGrid.stories.d.ts.map +1 -0
  20. package/dist/esm/stories/InteractiveImageGrid.stories.js +59 -0
  21. package/dist/esm/stories/InteractiveImageGrid.stories.js.map +1 -0
  22. package/dist/esm/styles/global.css +30 -0
  23. package/dist/sparkle.css +49 -28
  24. package/package.json +1 -1
  25. package/src/components/Breadcrumbs.tsx +35 -9
  26. package/src/components/InteractiveImageGrid.tsx +310 -0
  27. package/src/components/index.ts +1 -1
  28. package/src/stories/Breadcrumbs.stories.tsx +17 -6
  29. package/src/stories/InteractiveImageGrid.stories.tsx +70 -0
  30. package/src/styles/global.css +30 -0
  31. package/dist/esm/components/InteractiveImage.d.ts +0 -11
  32. package/dist/esm/components/InteractiveImage.d.ts.map +0 -1
  33. package/dist/esm/components/InteractiveImage.js +0 -73
  34. package/dist/esm/components/InteractiveImage.js.map +0 -1
  35. package/dist/esm/stories/InteractiveImage.stories.d.ts.map +0 -1
  36. package/dist/esm/stories/InteractiveImage.stories.js +0 -17
  37. package/dist/esm/stories/InteractiveImage.stories.js.map +0 -1
  38. package/src/components/InteractiveImage.tsx +0 -190
  39. package/src/stories/InteractiveImage.stories.tsx +0 -39
@@ -0,0 +1,70 @@
1
+ import type { Meta } from "@storybook/react";
2
+ import React from "react";
3
+
4
+ import { InteractiveImageGrid } from "@sparkle/components/InteractiveImageGrid";
5
+
6
+ import { Citation } from "../index_with_tw_base";
7
+
8
+ const meta = {
9
+ title: "Components/Citation",
10
+ component: Citation,
11
+ } satisfies Meta<typeof Citation>;
12
+
13
+ export default meta;
14
+
15
+ const images = [
16
+ {
17
+ alt: "Example of a loading interactive image",
18
+ isLoading: true,
19
+ title: "Example of a loading interactive image",
20
+ },
21
+ {
22
+ alt: "Example of an interactive image",
23
+ downloadUrl: "https://dust.tt/static/droidavatar/Droid_Lime_2.jpg",
24
+ imageUrl: "https://dust.tt/static/droidavatar/Droid_Lime_2.jpg",
25
+ title: "Example of an interactive image",
26
+ },
27
+ {
28
+ alt: "Example of an interactive image",
29
+ downloadUrl: "https://dust.tt/static/droidavatar/Droid_Lime_3.jpg",
30
+ imageUrl: "https://dust.tt/static/droidavatar/Droid_Lime_3.jpg",
31
+ title: "Example of an interactive image",
32
+ },
33
+ {
34
+ alt: "Example of an interactive image",
35
+ downloadUrl: "https://dust.tt/static/droidavatar/Droid_Lime_4.jpg",
36
+ imageUrl: "https://dust.tt/static/droidavatar/Droid_Lime_4.jpg",
37
+ title: "Example of an interactive image",
38
+ },
39
+ {
40
+ alt: "Example of an interactive image",
41
+ downloadUrl: "https://dust.tt/static/droidavatar/Droid_Lime_5.jpg",
42
+ imageUrl: "https://dust.tt/static/droidavatar/Droid_Lime_5.jpg",
43
+ title: "Example of an interactive image",
44
+ },
45
+ {
46
+ alt: "Example of an interactive image",
47
+ downloadUrl: "https://dust.tt/static/droidavatar/Droid_Lime_6.jpg",
48
+ imageUrl: "https://dust.tt/static/droidavatar/Droid_Lime_6.jpg",
49
+ title: "Example of an interactive image",
50
+ },
51
+ {
52
+ alt: "Example of an interactive PNG image",
53
+ downloadUrl: "https://dust.tt/static/DustHorizontalIcon.png",
54
+ imageUrl: "https://dust.tt/static/DustHorizontalIcon.png",
55
+ title: "Example of an interactive image",
56
+ },
57
+ ];
58
+
59
+ export const InteractiveImageExample = () => (
60
+ <div className="s-flex s-flex-col s-gap-8">
61
+ <div className="s-w-[700px]">
62
+ <h2>Interactive Image Grid</h2>
63
+ <InteractiveImageGrid images={images} />
64
+ </div>
65
+ <div className="s-w-[700px]">
66
+ <h2>Interactive Image Grid with 1 image</h2>
67
+ <InteractiveImageGrid images={images.slice(1, 2)} />
68
+ </div>
69
+ </div>
70
+ );
@@ -22,6 +22,36 @@
22
22
  }
23
23
  }
24
24
 
25
+ @layer utilities {
26
+ .s-checkerboard {
27
+ background-image: linear-gradient(
28
+ 45deg,
29
+ var(--tw-checker-color) 25%,
30
+ transparent 25%,
31
+ transparent 75%,
32
+ var(--tw-checker-color) 75%
33
+ ),
34
+ linear-gradient(
35
+ 45deg,
36
+ var(--tw-checker-color) 25%,
37
+ transparent 25%,
38
+ transparent 75%,
39
+ var(--tw-checker-color) 75%
40
+ );
41
+ background-size: 40px 40px;
42
+ background-position:
43
+ 0 0,
44
+ 20px 20px;
45
+ background-color: theme("colors.white"); /* Base color */
46
+ --tw-checker-color: theme("colors.gray.50"); /* Pattern color */
47
+ }
48
+
49
+ .s-dark .s-checkerboard {
50
+ background-color: theme("colors.black"); /* Dark mode base color */
51
+ --tw-checker-color: theme("colors.slate.950"); /* Dark mode pattern color */
52
+ }
53
+ }
54
+
25
55
  .s-blinking-cursor > :not(pre):last-child::after {
26
56
  content: "";
27
57
  width: 8px;
@@ -1,11 +0,0 @@
1
- import React from "react";
2
- interface InteractiveImageProps {
3
- alt: string;
4
- downloadUrl?: string;
5
- imageUrl?: string;
6
- isLoading?: boolean;
7
- title: string;
8
- }
9
- export declare function InteractiveImage({ alt, downloadUrl, imageUrl, isLoading, ...props }: InteractiveImageProps): React.JSX.Element;
10
- export {};
11
- //# sourceMappingURL=InteractiveImage.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"InteractiveImage.d.ts","sourceRoot":"","sources":["../../../src/components/InteractiveImage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAqD1B,UAAU,qBAAqB;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,GAAG,EACH,WAAW,EACX,QAAQ,EACR,SAAiB,EACjB,GAAG,KAAK,EACT,EAAE,qBAAqB,qBA2DvB"}
@@ -1,73 +0,0 @@
1
- import { __assign, __awaiter, __generator, __read, __rest } from "tslib";
2
- import React from "react";
3
- import { Dialog, DialogContent, DialogTrigger, IconButton, Spinner, } from "../components";
4
- import { ArrowDownOnSquareIcon } from "../icons/app";
5
- import { cn } from "../lib/utils";
6
- function DownloadButton(_a) {
7
- var _this = this;
8
- var className = _a.className, downloadUrl = _a.downloadUrl, _b = _a.size, size = _b === void 0 ? "xs" : _b, title = _a.title;
9
- var handleDownload = React.useCallback(function () { return __awaiter(_this, void 0, void 0, function () {
10
- var link;
11
- return __generator(this, function (_a) {
12
- if (!downloadUrl) {
13
- return [2 /*return*/];
14
- }
15
- link = document.createElement("a");
16
- link.href = downloadUrl;
17
- link.download = title;
18
- document.body.appendChild(link);
19
- link.click();
20
- document.body.removeChild(link);
21
- return [2 /*return*/];
22
- });
23
- }); }, [downloadUrl, title]);
24
- return (React.createElement(IconButton, { icon: ArrowDownOnSquareIcon, className: cn("s-text-white", className), tooltip: "Download", size: size, onClick: function (e) { return __awaiter(_this, void 0, void 0, function () {
25
- return __generator(this, function (_a) {
26
- switch (_a.label) {
27
- case 0:
28
- e.stopPropagation(); // Prevent image zoom.
29
- return [4 /*yield*/, handleDownload()];
30
- case 1:
31
- _a.sent();
32
- return [2 /*return*/];
33
- }
34
- });
35
- }); } }));
36
- }
37
- export function InteractiveImage(_a) {
38
- var alt = _a.alt, downloadUrl = _a.downloadUrl, imageUrl = _a.imageUrl, _b = _a.isLoading, isLoading = _b === void 0 ? false : _b, props = __rest(_a, ["alt", "downloadUrl", "imageUrl", "isLoading"]);
39
- var _c = __read(React.useState(false), 2), isZoomed = _c[0], setIsZoomed = _c[1];
40
- var _d = __read(React.useState(false), 2), imageLoaded = _d[0], setImageLoaded = _d[1];
41
- var handleZoomToggle = React.useCallback(function () {
42
- setIsZoomed(!isZoomed);
43
- }, [isZoomed]);
44
- return (React.createElement(Dialog, { open: isZoomed, onOpenChange: handleZoomToggle },
45
- React.createElement(DialogTrigger, { asChild: true },
46
- React.createElement("div", { className: "s-aspect-square s-h-80 s-w-80" },
47
- React.createElement(ImagePreview, __assign({ alt: alt, downloadUrl: downloadUrl !== null && downloadUrl !== void 0 ? downloadUrl : imageUrl, isLoading: isLoading, onClick: function (e) {
48
- if (isLoading) {
49
- e.preventDefault();
50
- e.stopPropagation();
51
- return;
52
- }
53
- handleZoomToggle();
54
- }, imageUrl: imageUrl }, props)))),
55
- React.createElement(DialogContent, { className: cn("s-w-auto s-max-w-none s-border-0 s-outline-none s-ring-0", "focus:s-outline-none focus:s-ring-0", "s-rounded-none s-bg-transparent s-shadow-none"), size: "xl" },
56
- React.createElement("div", { className: "s-flex s-flex-col" },
57
- React.createElement("div", { className: "s-flex s-justify-end" }, imageLoaded && (React.createElement(DownloadButton, { downloadUrl: downloadUrl !== null && downloadUrl !== void 0 ? downloadUrl : imageUrl, title: props.title, size: "md" }))),
58
- React.createElement("div", { className: "s-relative s-w-full" },
59
- React.createElement("img", { src: imageUrl, alt: alt, className: "s-w-full s-object-contain", onLoad: function () { return setImageLoaded(true); } }))))));
60
- }
61
- function LoadingImage() {
62
- return (React.createElement("div", { className: "s-flex s-h-full s-w-full s-items-center s-justify-center" },
63
- React.createElement(Spinner, { variant: "dark", size: "md" })));
64
- }
65
- function ImagePreview(_a) {
66
- var alt = _a.alt, downloadUrl = _a.downloadUrl, imageUrl = _a.imageUrl, isLoading = _a.isLoading, onClick = _a.onClick, title = _a.title;
67
- return (React.createElement("div", { onClick: onClick, className: cn("s-group/preview s-relative s-h-full s-w-full s-overflow-hidden s-rounded-2xl", "s-bg-muted-background dark:s-bg-muted-background-night", !isLoading && "s-cursor-pointer") }, isLoading ? (React.createElement(LoadingImage, null)) : (React.createElement(React.Fragment, null,
68
- React.createElement("img", { src: imageUrl, alt: alt, className: "s-h-full s-w-full s-rounded-2xl s-object-cover" }),
69
- React.createElement("div", { className: cn("s-absolute s-inset-0 s-bg-gradient-to-b", "s-from-black/40 s-via-transparent s-to-black/40", "s-opacity-0 s-transition-opacity s-duration-200", "group-hover/preview:s-opacity-100") }),
70
- React.createElement("div", { className: cn("s-absolute s-right-3 s-top-3 s-z-10 s-flex", "s-opacity-0 s-transition-opacity s-duration-200", "group-hover/preview:s-opacity-100") },
71
- React.createElement(DownloadButton, { downloadUrl: downloadUrl, title: title, size: "xs" }))))));
72
- }
73
- //# sourceMappingURL=InteractiveImage.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"InteractiveImage.js","sourceRoot":"","sources":["../../../src/components/InteractiveImage.tsx"],"names":[],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"InteractiveImage.stories.d.ts","sourceRoot":"","sources":["../../../src/stories/InteractiveImage.stories.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,OAAO,CAAC;AAM1B,QAAA,MAAM,IAAI;;;;;;;CAGuB,CAAC;AAElC,eAAe,IAAI,CAAC;AAEpB,eAAO,MAAM,uBAAuB,yBAwBnC,CAAC"}
@@ -1,17 +0,0 @@
1
- import React from "react";
2
- import { InteractiveImage } from "../components/InteractiveImage";
3
- import { Citation } from "../index_with_tw_base";
4
- var meta = {
5
- title: "Components/Citation",
6
- component: Citation,
7
- };
8
- export default meta;
9
- export var InteractiveImageExample = function () { return (React.createElement("div", { className: "s-flex s-flex-col s-gap-8" },
10
- "Example of interactive image",
11
- React.createElement("h2", null, "Loading"),
12
- React.createElement(InteractiveImage, { alt: "Example of a loading interactive image", isLoading: true, title: "Example of a loading interactive image" }),
13
- React.createElement("h2", null, "Loaded"),
14
- React.createElement(InteractiveImage, { alt: "Example of an interactive image", downloadUrl: "https://dust.tt/static/droidavatar/Droid_Lime_3.jpg", imageUrl: "https://dust.tt/static/droidavatar/Droid_Lime_3.jpg", title: "Example of an interactive image" }),
15
- React.createElement("h3", null, "With 4:3 aspect ratio"),
16
- React.createElement(InteractiveImage, { alt: "Example of a 4:3 aspect ratio interactive image", downloadUrl: "https://upload.wikimedia.org/wikipedia/commons/d/de/Aspect-ratio-4x3.svg", imageUrl: "https://upload.wikimedia.org/wikipedia/commons/d/de/Aspect-ratio-4x3.svg", title: "Example of a 4:3 aspect ratio interactive image" }))); };
17
- //# sourceMappingURL=InteractiveImage.stories.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"InteractiveImage.stories.js","sourceRoot":"","sources":["../../../src/stories/InteractiveImage.stories.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AAExE,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAEjD,IAAM,IAAI,GAAG;IACX,KAAK,EAAE,qBAAqB;IAC5B,SAAS,EAAE,QAAQ;CACY,CAAC;AAElC,eAAe,IAAI,CAAC;AAEpB,MAAM,CAAC,IAAM,uBAAuB,GAAG,cAAM,OAAA,CAC3C,6BAAK,SAAS,EAAC,2BAA2B;;IAExC,0CAAgB;IAChB,oBAAC,gBAAgB,IACf,GAAG,EAAC,wCAAwC,EAC5C,SAAS,EAAE,IAAI,EACf,KAAK,EAAC,wCAAwC,GAC9C;IACF,yCAAe;IACf,oBAAC,gBAAgB,IACf,GAAG,EAAC,iCAAiC,EACrC,WAAW,EAAC,qDAAqD,EACjE,QAAQ,EAAC,qDAAqD,EAC9D,KAAK,EAAC,iCAAiC,GACvC;IACF,wDAA8B;IAC9B,oBAAC,gBAAgB,IACf,GAAG,EAAC,iDAAiD,EACrD,WAAW,EAAC,0EAA0E,EACtF,QAAQ,EAAC,0EAA0E,EACnF,KAAK,EAAC,iDAAiD,GACvD,CACE,CACP,EAxB4C,CAwB5C,CAAC"}
@@ -1,190 +0,0 @@
1
- import React from "react";
2
-
3
- import {
4
- Dialog,
5
- DialogContent,
6
- DialogTrigger,
7
- IconButton,
8
- Spinner,
9
- } from "@sparkle/components/";
10
- import { ArrowDownOnSquareIcon } from "@sparkle/icons/app";
11
- import { cn } from "@sparkle/lib/utils";
12
-
13
- interface DownloadButtonProps {
14
- className?: string;
15
- downloadUrl?: string;
16
- size?: "xs" | "sm" | "md";
17
- title: string;
18
- }
19
-
20
- function DownloadButton({
21
- className,
22
- downloadUrl,
23
- size = "xs",
24
- title,
25
- }: DownloadButtonProps) {
26
- const handleDownload = React.useCallback(async () => {
27
- if (!downloadUrl) {
28
- return;
29
- }
30
-
31
- // Create a hidden link and click it.
32
- const link = document.createElement("a");
33
- link.href = downloadUrl;
34
- link.download = title;
35
- document.body.appendChild(link);
36
- link.click();
37
- document.body.removeChild(link);
38
- }, [downloadUrl, title]);
39
-
40
- return (
41
- <IconButton
42
- icon={ArrowDownOnSquareIcon}
43
- className={cn("s-text-white", className)}
44
- tooltip="Download"
45
- size={size}
46
- onClick={async (e) => {
47
- e.stopPropagation(); // Prevent image zoom.
48
- await handleDownload();
49
- }}
50
- />
51
- );
52
- }
53
-
54
- interface InteractiveImageProps {
55
- alt: string;
56
- downloadUrl?: string;
57
- imageUrl?: string;
58
- isLoading?: boolean;
59
- title: string;
60
- }
61
-
62
- export function InteractiveImage({
63
- alt,
64
- downloadUrl,
65
- imageUrl,
66
- isLoading = false,
67
- ...props
68
- }: InteractiveImageProps) {
69
- const [isZoomed, setIsZoomed] = React.useState(false);
70
- const [imageLoaded, setImageLoaded] = React.useState(false);
71
-
72
- const handleZoomToggle = React.useCallback(() => {
73
- setIsZoomed(!isZoomed);
74
- }, [isZoomed]);
75
-
76
- return (
77
- <Dialog open={isZoomed} onOpenChange={handleZoomToggle}>
78
- <DialogTrigger asChild>
79
- <div className="s-aspect-square s-h-80 s-w-80">
80
- <ImagePreview
81
- alt={alt}
82
- downloadUrl={downloadUrl ?? imageUrl}
83
- isLoading={isLoading}
84
- onClick={(e) => {
85
- if (isLoading) {
86
- e.preventDefault();
87
- e.stopPropagation();
88
- return;
89
- }
90
- handleZoomToggle();
91
- }}
92
- imageUrl={imageUrl}
93
- {...props}
94
- />
95
- </div>
96
- </DialogTrigger>
97
- <DialogContent
98
- className={cn(
99
- "s-w-auto s-max-w-none s-border-0 s-outline-none s-ring-0",
100
- "focus:s-outline-none focus:s-ring-0",
101
- "s-rounded-none s-bg-transparent s-shadow-none"
102
- )}
103
- size="xl"
104
- >
105
- <div className="s-flex s-flex-col">
106
- <div className="s-flex s-justify-end">
107
- {imageLoaded && (
108
- <DownloadButton
109
- downloadUrl={downloadUrl ?? imageUrl}
110
- title={props.title}
111
- size="md"
112
- />
113
- )}
114
- </div>
115
- <div className="s-relative s-w-full">
116
- <img
117
- src={imageUrl}
118
- alt={alt}
119
- className="s-w-full s-object-contain"
120
- onLoad={() => setImageLoaded(true)}
121
- />
122
- </div>
123
- </div>
124
- </DialogContent>
125
- </Dialog>
126
- );
127
- }
128
-
129
- function LoadingImage() {
130
- return (
131
- <div className="s-flex s-h-full s-w-full s-items-center s-justify-center">
132
- <Spinner variant="dark" size="md" />
133
- </div>
134
- );
135
- }
136
-
137
- type ImagePreviewProps = InteractiveImageProps & {
138
- onClick?: (e: React.MouseEvent) => void;
139
- };
140
-
141
- function ImagePreview({
142
- alt,
143
- downloadUrl,
144
- imageUrl,
145
- isLoading,
146
- onClick,
147
- title,
148
- }: ImagePreviewProps) {
149
- return (
150
- <div
151
- onClick={onClick}
152
- className={cn(
153
- "s-group/preview s-relative s-h-full s-w-full s-overflow-hidden s-rounded-2xl",
154
- "s-bg-muted-background dark:s-bg-muted-background-night",
155
- !isLoading && "s-cursor-pointer"
156
- )}
157
- >
158
- {isLoading ? (
159
- <LoadingImage />
160
- ) : (
161
- <>
162
- <img
163
- src={imageUrl}
164
- alt={alt}
165
- className="s-h-full s-w-full s-rounded-2xl s-object-cover"
166
- />
167
- {/* Dark overlay on hover */}
168
- <div
169
- className={cn(
170
- "s-absolute s-inset-0 s-bg-gradient-to-b",
171
- "s-from-black/40 s-via-transparent s-to-black/40",
172
- "s-opacity-0 s-transition-opacity s-duration-200",
173
- "group-hover/preview:s-opacity-100"
174
- )}
175
- />
176
- {/* Icon container - only visible on hover */}
177
- <div
178
- className={cn(
179
- "s-absolute s-right-3 s-top-3 s-z-10 s-flex",
180
- "s-opacity-0 s-transition-opacity s-duration-200",
181
- "group-hover/preview:s-opacity-100"
182
- )}
183
- >
184
- <DownloadButton downloadUrl={downloadUrl} title={title} size="xs" />
185
- </div>
186
- </>
187
- )}
188
- </div>
189
- );
190
- }
@@ -1,39 +0,0 @@
1
- import type { Meta } from "@storybook/react";
2
- import React from "react";
3
-
4
- import { InteractiveImage } from "@sparkle/components/InteractiveImage";
5
-
6
- import { Citation } from "../index_with_tw_base";
7
-
8
- const meta = {
9
- title: "Components/Citation",
10
- component: Citation,
11
- } satisfies Meta<typeof Citation>;
12
-
13
- export default meta;
14
-
15
- export const InteractiveImageExample = () => (
16
- <div className="s-flex s-flex-col s-gap-8">
17
- Example of interactive image
18
- <h2>Loading</h2>
19
- <InteractiveImage
20
- alt="Example of a loading interactive image"
21
- isLoading={true}
22
- title="Example of a loading interactive image"
23
- />
24
- <h2>Loaded</h2>
25
- <InteractiveImage
26
- alt="Example of an interactive image"
27
- downloadUrl="https://dust.tt/static/droidavatar/Droid_Lime_3.jpg"
28
- imageUrl="https://dust.tt/static/droidavatar/Droid_Lime_3.jpg"
29
- title="Example of an interactive image"
30
- />
31
- <h3>With 4:3 aspect ratio</h3>
32
- <InteractiveImage
33
- alt="Example of a 4:3 aspect ratio interactive image"
34
- downloadUrl="https://upload.wikimedia.org/wikipedia/commons/d/de/Aspect-ratio-4x3.svg"
35
- imageUrl="https://upload.wikimedia.org/wikipedia/commons/d/de/Aspect-ratio-4x3.svg"
36
- title="Example of a 4:3 aspect ratio interactive image"
37
- />
38
- </div>
39
- );