@synerise/ds-image 2.0.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,12 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [2.0.1](https://github.com/Synerise/synerise-design/compare/@synerise/ds-image@2.0.0...@synerise/ds-image@2.0.1) (2026-09-01)
7
+
8
+ ### Bug Fixes
9
+
10
+ - **image:** keep the preview tooltips above a raised overlay ([9440cba](https://github.com/Synerise/synerise-design/commit/9440cba7d71bc9cf2d003aa179d69d67804e4e9b))
11
+
6
12
  # [2.0.0](https://github.com/Synerise/synerise-design/compare/@synerise/ds-image@0.2.0...@synerise/ds-image@2.0.0) (2026-08-26)
7
13
 
8
14
  **Note:** Version bump only for package @synerise/ds-image
package/CLAUDE.md CHANGED
@@ -124,7 +124,10 @@ tables.
124
124
  top-right (`CloseWrapper`). The download control is a `ToolbarButton` rendered
125
125
  as an anchor (`href`/`download`/`target="_blank"` forwarded to `<a>` by
126
126
  `ds-button`) so it downloads same-origin images and opens cross-origin ones in
127
- a new tab; it shows whenever a valid image is loaded (independent of zoom).
127
+ a new tab; it shows whenever a valid image is loaded (independent of zoom) and
128
+ the image is downloadable — per-image `ImageSource.downloadable` → preview
129
+ `downloadable` (default `true`). Hiding it leaves the rest of the zoom group
130
+ intact, and the whole group drops out only when nothing in it renders.
128
131
  - **Error/Empty fallback** — broken `src` → `fallback` (per-image `ImageSource.fallback`
129
132
  → component `fallback` → default `ImageM` placeholder). No `src` on a Thumbnail →
130
133
  the Empty placeholder.
package/README.md CHANGED
@@ -87,6 +87,7 @@ enclosing `Gallery` before the default listed above.
87
87
  | onDelete | Called with the index of the thumbnail whose delete fired. | (index: number) => void | --- |
88
88
  | openZoom | Whether clicking a thumbnail opens the preview. | boolean | `true` |
89
89
  | zoomable / zoomStep / maxZoom / initialZoom | Forwarded to the preview it owns. | see ImagePreview | see ImagePreview |
90
+ | downloadable | Forwarded to the preview — render the download control. | boolean | `true` |
90
91
  | getContainer / zIndex | Forwarded to the preview it owns. | see ImagePreview | see ImagePreview |
91
92
  | texts | Override tooltips / accessible labels for thumbnails and the preview. | Partial&lt;ImageTexts&gt; | DS translations |
92
93
  | className | Class applied to the gallery container. | string | --- |
@@ -106,6 +107,7 @@ enclosing `Gallery` before the default listed above.
106
107
  | initialZoom | Where zoom starts. | 'fit' / 'real-size' | `'fit'` |
107
108
  | closable | Whether to render the close button and allow Escape to close. | boolean | `true` |
108
109
  | maskClosable | Whether clicking the dimmed backdrop closes the viewer. | boolean | `true` |
110
+ | downloadable | Whether to render the download control (per-image override via `ImageSource.downloadable`). | boolean | `true` |
109
111
  | zIndex | z-index of the overlay. Omitted, it stacks one step above the enclosing modal / drawer. Set it to opt out of the DS stack. | number | `zindex-modal` |
110
112
  | fallback | Rendered when an image fails to load (per-image override via `ImageSource.fallback`). | React.ReactNode | broken-image box |
111
113
  | getContainer | Portal target. | () => HTMLElement | `document.body` |
@@ -1,3 +1,3 @@
1
1
  import { GalleryProps } from './Gallery.types';
2
- declare const Gallery: ({ images, aspectRatio, size, height, background, objectFit, loading, fallback, deletable, onDelete, openZoom, zoomable, zoomStep, maxZoom, initialZoom, getContainer, zIndex, texts, className, }: GalleryProps) => JSX.Element;
2
+ declare const Gallery: ({ images, aspectRatio, size, height, background, objectFit, loading, fallback, deletable, onDelete, openZoom, zoomable, zoomStep, maxZoom, initialZoom, downloadable, getContainer, zIndex, texts, className, }: GalleryProps) => JSX.Element;
3
3
  export default Gallery;
@@ -20,6 +20,7 @@ const Gallery = ({
20
20
  zoomStep,
21
21
  maxZoom,
22
22
  initialZoom,
23
+ downloadable,
23
24
  getContainer,
24
25
  zIndex,
25
26
  texts,
@@ -38,7 +39,7 @@ const Gallery = ({
38
39
  }), [aspectRatio, size, height, background, objectFit, loading, fallback, texts]);
39
40
  return /* @__PURE__ */ jsxs(ImageGalleryContext.Provider, { value: contextValue, children: [
40
41
  /* @__PURE__ */ jsx(Container, { className, "data-testid": "image-gallery", children: images.map((image, imageIndex) => /* @__PURE__ */ jsx(Thumbnail, { src: image.src, alt: image.alt, fallback: image.fallback, deletable, onDelete: onDelete ? () => onDelete(imageIndex) : void 0, openZoom, onClick: openZoom ? () => setPreviewIndex(imageIndex) : void 0 }, `${image.src}-${imageIndex}`)) }),
41
- openZoom && /* @__PURE__ */ jsx(ImagePreview, { open: previewIndex !== null, images, index: previewIndex ?? 0, onIndexChange: setPreviewIndex, onClose: () => setPreviewIndex(null), zoomable, zoomStep, maxZoom, initialZoom, fallback, getContainer, zIndex, texts })
42
+ openZoom && /* @__PURE__ */ jsx(ImagePreview, { open: previewIndex !== null, images, index: previewIndex ?? 0, onIndexChange: setPreviewIndex, onClose: () => setPreviewIndex(null), zoomable, zoomStep, maxZoom, initialZoom, downloadable, fallback, getContainer, zIndex, texts })
42
43
  ] });
43
44
  };
44
45
  export {
@@ -31,6 +31,8 @@ export type GalleryProps = {
31
31
  maxZoom?: number;
32
32
  /** Preview pass-through — where zoom starts. */
33
33
  initialZoom?: InitialZoom;
34
+ /** Preview pass-through — render the download control. */
35
+ downloadable?: boolean;
34
36
  /** Preview pass-through — portal target. */
35
37
  getContainer?: () => HTMLElement;
36
38
  /** Preview pass-through — z-index of the preview overlay. */
@@ -1,4 +1,4 @@
1
1
  import { default as React } from 'react';
2
2
  import { ImagePreviewProps } from './ImagePreview.types';
3
- declare const ImagePreview: ({ open, images, index, onIndexChange, onClose, zoomable, zoomStep, maxZoom, initialZoom, closable, maskClosable, fallback, getContainer, destroyOnClose, onZoom, texts: textsProp, zIndex, }: ImagePreviewProps) => React.ReactPortal | null;
3
+ declare const ImagePreview: ({ open, images, index, onIndexChange, onClose, zoomable, zoomStep, maxZoom, initialZoom, closable, maskClosable, downloadable, fallback, getContainer, destroyOnClose, onZoom, texts: textsProp, zIndex, }: ImagePreviewProps) => React.ReactPortal | null;
4
4
  export default ImagePreview;
@@ -36,6 +36,7 @@ const ImagePreview = ({
36
36
  initialZoom = "fit",
37
37
  closable = true,
38
38
  maskClosable = true,
39
+ downloadable = true,
39
40
  fallback,
40
41
  getContainer,
41
42
  destroyOnClose = false,
@@ -178,10 +179,10 @@ const ImagePreview = ({
178
179
  };
179
180
  const zoomPercent = naturalScaleFactor > 0 ? Math.round(scale / naturalScaleFactor * 100) : 100;
180
181
  const hasError = status === "error" || !currentImage;
181
- const showDownload = !hasError && Boolean(currentSrc);
182
+ const showDownload = (currentImage?.downloadable ?? downloadable) && !hasError && Boolean(currentSrc);
182
183
  const image = /* @__PURE__ */ jsx(Image, { ref: imgRef, src: currentSrc, alt: currentImage?.alt ?? "", onLoad: handleImageLoad, onError: handleError, onClick: stopPropagation, draggable: false, "data-testid": "image-preview-image" }, index);
183
184
  const content = hasError ? /* @__PURE__ */ jsx(Fallback, { onClick: stopPropagation, "data-testid": "image-preview-fallback", children: currentImage?.fallback ?? fallback ?? /* @__PURE__ */ jsx(FallbackBox, { children: /* @__PURE__ */ jsx(Icon, { component: /* @__PURE__ */ jsx(ImageM, {}) }) }) }) : image;
184
- return createPortal(/* @__PURE__ */ jsxs(Overlay, { ref: overlayRef, role: "dialog", "aria-modal": true, "aria-label": currentImage?.alt, tabIndex: -1, $hidden: !open, $zIndex: resolvedZIndex, onMouseDown: handleOverlayPointerDown, onClick: handleMaskClick, "data-testid": "image-preview", children: [
185
+ return createPortal(/* @__PURE__ */ jsxs(Overlay, { ref: overlayRef, role: "dialog", "aria-modal": true, "aria-label": currentImage?.alt, tabIndex: -1, $hidden: !open, $zIndex: resolvedZIndex, "data-popup-container": true, onMouseDown: handleOverlayPointerDown, onClick: handleMaskClick, "data-testid": "image-preview", children: [
185
186
  /* @__PURE__ */ jsx(ImageWrapper, { ref: workingAreaRef, children: zoomable && !hasError ? /* @__PURE__ */ jsx(TransformWrapper, { ref: transformRef, minScale: FIT_SCALE, maxScale, initialScale: FIT_SCALE, centerOnInit: true, doubleClick: {
186
187
  mode: "reset"
187
188
  }, wheel: {
@@ -26,6 +26,11 @@ export type ImagePreviewProps = {
26
26
  closable?: boolean;
27
27
  /** Whether clicking the dimmed backdrop closes the viewer. Defaults to true. */
28
28
  maskClosable?: boolean;
29
+ /**
30
+ * Whether to render the download control. Defaults to true. Override it for a
31
+ * single image with `ImageSource.downloadable`.
32
+ */
33
+ downloadable?: boolean;
29
34
  /** Fallback rendered when an image fails to load (per-image override via `ImageSource.fallback`). */
30
35
  fallback?: ReactNode;
31
36
  /** Portal target. Defaults to `document.body`. */
@@ -7,6 +7,11 @@ export type ImageSource = {
7
7
  alt: string;
8
8
  /** Per-image override for the broken-image fallback. */
9
9
  fallback?: ReactNode;
10
+ /**
11
+ * Per-image override for the preview's download control. Takes precedence
12
+ * over the preview-level `downloadable` prop.
13
+ */
14
+ downloadable?: boolean;
10
15
  };
11
16
  /**
12
17
  * Aspect ratio applied to a thumbnail. `source` keeps the image's intrinsic
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@synerise/ds-image",
3
- "version": "2.0.0",
3
+ "version": "2.0.1",
4
4
  "description": "Image UI Component for the Synerise Design System",
5
5
  "license": "ISC",
6
6
  "repository": "Synerise/synerise-design",
@@ -55,5 +55,5 @@
55
55
  "styled-components": "^5.3.3",
56
56
  "vitest": "4"
57
57
  },
58
- "gitHead": "033854a18f038079e9a16b0ff250f8401a2287c2"
58
+ "gitHead": "a57eb7eaf98318b364468368d1aa4fd9fa6a6dc8"
59
59
  }