@vectara/vectara-ui 9.16.1 → 9.17.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.
@@ -7,5 +7,5 @@ type Props = {
7
7
  isFailure?: boolean;
8
8
  allowPreview?: boolean;
9
9
  };
10
- export declare const VuiImage: ({ src, alt, caption, className, isLoading, allowPreview, isFailure, }: Props) => import("react/jsx-runtime").JSX.Element;
10
+ export declare const VuiImage: ({ src, alt, caption, className, isLoading, allowPreview, isFailure }: Props) => import("react/jsx-runtime").JSX.Element;
11
11
  export {};
@@ -7,7 +7,7 @@ import { VuiText } from "../typography/Text";
7
7
  import { VuiImagePreview } from "./ImagePreview";
8
8
  import { VuiIcon } from "../icon/Icon";
9
9
  import { BiImage, BiError } from "react-icons/bi";
10
- export const VuiImage = ({ src, alt, caption, className, isLoading = false, allowPreview = true, isFailure = false, }) => {
10
+ export const VuiImage = ({ src, alt, caption, className, isLoading = false, allowPreview = true, isFailure = false }) => {
11
11
  const [isPreviewOpen, setIsPreviewOpen] = useState(false);
12
12
  const handlePreviewClick = () => {
13
13
  if (allowPreview) {
@@ -23,5 +23,5 @@ export const VuiImage = ({ src, alt, caption, className, isLoading = false, allo
23
23
  if (isFailure) {
24
24
  return (_jsx("div", Object.assign({ className: "vuiImage__placeholder vuiImage__placeholder--error" }, { children: _jsxs(VuiFlexContainer, Object.assign({ direction: "column", alignItems: "center", justifyContent: "center", spacing: "s" }, { children: [_jsx(VuiFlexItem, Object.assign({ grow: false }, { children: _jsx("div", Object.assign({ className: "vuiImage__iconWrapper" }, { children: _jsx(VuiIcon, Object.assign({ size: "m", color: "danger" }, { children: _jsx(BiError, {}) })) })) })), _jsx(VuiFlexItem, Object.assign({ grow: false }, { children: _jsx(VuiText, Object.assign({ size: "s", align: "center" }, { children: _jsx("p", { children: "Missing image" }) })) }))] })) })));
25
25
  }
26
- return (_jsxs(_Fragment, { children: [_jsx(VuiFlexContainer, Object.assign({ direction: "column", spacing: "s" }, { children: _jsxs("figure", { children: [_jsx(VuiFlexItem, Object.assign({ grow: false }, { children: _jsx("div", Object.assign({ className: "vuiImage__imageWrapper" }, { children: _jsx("img", { src: src, alt: alt, className: imageClasses, onClick: handlePreviewClick }) })) })), caption && (_jsx(VuiFlexItem, Object.assign({ grow: false }, { children: _jsx(VuiText, Object.assign({ size: "s" }, { children: _jsx("figcaption", { children: caption }) })) })))] }) })), allowPreview && (_jsx(VuiImagePreview, { src: src, alt: alt, isOpen: isPreviewOpen, onClose: () => setIsPreviewOpen(false) }))] }));
26
+ return (_jsxs(_Fragment, { children: [_jsx(VuiFlexContainer, Object.assign({ direction: "column", spacing: "s" }, { children: _jsxs("figure", { children: [_jsx(VuiFlexItem, Object.assign({ grow: false }, { children: _jsx("div", Object.assign({ className: "vuiImage__imageWrapper" }, { children: _jsx("img", { src: src, alt: alt, className: imageClasses, onClick: handlePreviewClick }) })) })), caption && (_jsx(VuiFlexItem, Object.assign({ grow: false }, { children: _jsx(VuiText, Object.assign({ size: "s" }, { children: _jsx("figcaption", { children: caption }) })) })))] }) })), allowPreview && (_jsx(VuiImagePreview, { images: [{ src, alt, caption }], isOpen: isPreviewOpen, onClose: () => setIsPreviewOpen(false) }))] }));
27
27
  };
@@ -1,9 +1,14 @@
1
- type Props = {
1
+ type ImageData = {
2
2
  src: string;
3
3
  alt?: string;
4
+ caption?: string;
5
+ };
6
+ type Props = {
7
+ images: ImageData[];
8
+ initialIndex?: number;
4
9
  isOpen: boolean;
5
10
  onClose: () => void;
6
11
  className?: string;
7
12
  };
8
- export declare const VuiImagePreview: ({ src, alt, isOpen, onClose, className }: Props) => import("react/jsx-runtime").JSX.Element;
13
+ export declare const VuiImagePreview: ({ images, initialIndex, isOpen, onClose, className }: Props) => import("react/jsx-runtime").JSX.Element;
9
14
  export {};
@@ -1,27 +1,59 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useEffect, useRef } from "react";
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useRef, useState } from "react";
3
3
  import { FocusOn } from "react-focus-on";
4
- import { BiX } from "react-icons/bi";
4
+ import { BiX, BiChevronLeft, BiChevronRight } from "react-icons/bi";
5
5
  import { VuiIconButton } from "../button/IconButton";
6
6
  import { VuiIcon } from "../icon/Icon";
7
7
  import { VuiPortal } from "../portal/Portal";
8
- import { VuiScreenBlock } from "../screenBlock/ScreenBlock";
9
- export const VuiImagePreview = ({ src, alt, isOpen, onClose, className }) => {
8
+ import { VuiFlexContainer } from "../flex/FlexContainer";
9
+ import { VuiFlexItem } from "../flex/FlexItem";
10
+ import { VuiText } from "../typography/Text";
11
+ import { VuiTextColor } from "../typography/TextColor";
12
+ export const VuiImagePreview = ({ images, initialIndex = 0, isOpen, onClose, className }) => {
10
13
  const returnFocusElRef = useRef(null);
11
- // Return focus on unmount
14
+ const [currentIndex, setCurrentIndex] = useState(initialIndex);
15
+ const isCarousel = images.length > 1;
12
16
  useEffect(() => {
13
17
  var _a;
14
18
  if (isOpen) {
15
19
  returnFocusElRef.current = document.activeElement;
20
+ setCurrentIndex(initialIndex);
16
21
  }
17
22
  else {
18
23
  (_a = returnFocusElRef.current) === null || _a === void 0 ? void 0 : _a.focus();
19
24
  returnFocusElRef.current = null;
20
25
  }
21
- }, [isOpen]);
22
- // Allow contents to respond to blur events before unmounting
26
+ }, [isOpen, initialIndex]);
27
+ useEffect(() => {
28
+ if (!isOpen)
29
+ return;
30
+ const handleKeyDown = (e) => {
31
+ if (e.key === "ArrowLeft") {
32
+ e.preventDefault();
33
+ handlePrevious();
34
+ }
35
+ else if (e.key === "ArrowRight") {
36
+ e.preventDefault();
37
+ handleNext();
38
+ }
39
+ };
40
+ window.addEventListener("keydown", handleKeyDown);
41
+ return () => window.removeEventListener("keydown", handleKeyDown);
42
+ }, [isOpen, currentIndex]);
43
+ const handlePrevious = () => {
44
+ setCurrentIndex((prev) => (prev > 0 ? prev - 1 : images.length - 1));
45
+ };
46
+ const handleNext = () => {
47
+ setCurrentIndex((prev) => (prev < images.length - 1 ? prev + 1 : 0));
48
+ };
23
49
  const handleOnClose = () => {
24
50
  onClose === null || onClose === void 0 ? void 0 : onClose();
25
51
  };
26
- return (_jsx(VuiPortal, { children: isOpen && (_jsx("div", Object.assign({ className: className }, { children: _jsx(FocusOn, Object.assign({ onEscapeKey: handleOnClose, returnFocus: false, autoFocus: isOpen }, { children: _jsx(VuiScreenBlock, Object.assign({ onClick: handleOnClose }, { children: _jsxs("div", Object.assign({ className: "vuiImagePreview__container" }, { children: [_jsx("div", Object.assign({ className: "vuiImagePreview__closeButton" }, { children: _jsx(VuiIconButton, { "aria-label": "Close preview", onClick: handleOnClose, color: "neutral", icon: _jsx(VuiIcon, Object.assign({ size: "l", color: "empty" }, { children: _jsx(BiX, {}) })) }) })), _jsx("div", Object.assign({ className: "vuiImagePreview__imageContainer" }, { children: _jsx("img", { src: src, alt: alt, className: "vuiImagePreview__image", draggable: false }) }))] })) })) })) }))) }));
52
+ return (_jsx(VuiPortal, { children: isOpen && (_jsx("div", Object.assign({ className: className }, { children: _jsx(FocusOn, Object.assign({ onEscapeKey: handleOnClose, returnFocus: false, autoFocus: isOpen }, { children: _jsx("figure", { children: _jsx("div", Object.assign({ className: "vuiImagePreview__container" }, { children: _jsxs("div", Object.assign({ className: "vuiImagePreview__mask", onClick: handleOnClose }, { children: [_jsx("div", Object.assign({ onClick: (e) => e.stopPropagation() }, { children: _jsxs(VuiFlexContainer, Object.assign({ alignItems: "center", justifyContent: "spaceBetween", className: "vuiImagePreview__header" }, { children: [_jsx(VuiFlexItem, { children: _jsxs(VuiFlexContainer, Object.assign({ alignItems: "center", spacing: "xs" }, { children: [isCarousel && (_jsxs(_Fragment, { children: [_jsx(VuiFlexItem, { children: _jsx(VuiIconButton, { "aria-label": "Previous image", onClick: (e) => {
53
+ e.stopPropagation();
54
+ handlePrevious();
55
+ }, color: "neutral", size: "s", icon: _jsx(VuiIcon, Object.assign({ size: "m", color: "empty" }, { children: _jsx(BiChevronLeft, {}) })) }) }), _jsx(VuiFlexItem, { children: _jsx(VuiIconButton, { "aria-label": "Next image", onClick: (e) => {
56
+ e.stopPropagation();
57
+ handleNext();
58
+ }, color: "neutral", size: "s", icon: _jsx(VuiIcon, Object.assign({ size: "m", color: "empty" }, { children: _jsx(BiChevronRight, {}) })) }) })] })), _jsx(VuiFlexItem, { children: _jsx(VuiText, Object.assign({ size: "s" }, { children: _jsx(VuiTextColor, Object.assign({ color: "empty" }, { children: _jsxs("figcaption", { children: [isCarousel && `Image ${currentIndex + 1} of ${images.length}: `, images[currentIndex].caption] }) })) })) })] })) }), _jsx(VuiFlexItem, Object.assign({ grow: false }, { children: _jsx("div", Object.assign({ className: "vuiImagePreview__closeButton" }, { children: _jsx(VuiIconButton, { "aria-label": "Close preview", onClick: handleOnClose, color: "neutral", icon: _jsx(VuiIcon, Object.assign({ size: "l", color: "empty" }, { children: _jsx(BiX, {}) })) }) })) }))] })) })), _jsx("div", Object.assign({ className: "vuiImagePreview__imageContainer" }, { children: _jsx("img", { src: images[currentIndex].src, alt: images[currentIndex].alt, className: "vuiImagePreview__image", draggable: false }) }))] })) })) }) })) }))) }));
27
59
  };
@@ -47,32 +47,43 @@
47
47
  left: 0;
48
48
  right: 0;
49
49
  bottom: 0;
50
- pointer-events: none;
50
+ z-index: $screenBlockZIndex;
51
51
  }
52
52
 
53
- .vuiImagePreview__closeButton {
53
+ .vuiImagePreview__mask {
54
+ height: 100%;
55
+ background-color: transparentize($colorFullShade, 0.4);
56
+ }
57
+
58
+ .vuiImagePreview__header {
54
59
  position: absolute;
55
- top: $sizeM;
56
- right: $sizeM;
60
+ top: 0;
61
+ left: 0;
62
+ right: 0;
63
+ height: $sizeL * 2;
64
+ padding: $sizeS $sizeM;
65
+ background-color: $colorFullShade;
66
+ }
67
+
68
+ .vuiImagePreview__closeButton {
57
69
  pointer-events: all;
58
- z-index: 1;
59
70
  }
60
71
 
61
72
  .vuiImagePreview__imageContainer {
62
73
  position: absolute;
63
- top: 0;
74
+ top: $sizeL * 2;
64
75
  left: 0;
65
76
  right: 0;
66
- bottom: 0;
67
77
  display: flex;
68
78
  align-items: center;
69
79
  justify-content: center;
70
80
  overflow: visible;
81
+ height: calc(100vh - #{$sizeL * 2});
71
82
  }
72
83
 
73
84
  .vuiImagePreview__image {
74
85
  max-width: calc(100vw - #{$sizeXl * 2});
75
- max-height: calc(100vh - #{$sizeXl * 2});
86
+ max-height: calc(100vh - #{$sizeL * 4});
76
87
  width: auto;
77
88
  height: auto;
78
89
  object-fit: contain;
@@ -3002,32 +3002,43 @@ h2.react-datepicker__current-month {
3002
3002
  left: 0;
3003
3003
  right: 0;
3004
3004
  bottom: 0;
3005
- pointer-events: none;
3005
+ z-index: 10;
3006
3006
  }
3007
3007
 
3008
- .vuiImagePreview__closeButton {
3008
+ .vuiImagePreview__mask {
3009
+ height: 100%;
3010
+ background-color: rgba(11, 12, 14, 0.6);
3011
+ }
3012
+
3013
+ .vuiImagePreview__header {
3009
3014
  position: absolute;
3010
- top: 16px;
3011
- right: 16px;
3015
+ top: 0;
3016
+ left: 0;
3017
+ right: 0;
3018
+ height: 48px;
3019
+ padding: 12px 16px;
3020
+ background-color: #0b0c0e;
3021
+ }
3022
+
3023
+ .vuiImagePreview__closeButton {
3012
3024
  pointer-events: all;
3013
- z-index: 1;
3014
3025
  }
3015
3026
 
3016
3027
  .vuiImagePreview__imageContainer {
3017
3028
  position: absolute;
3018
- top: 0;
3029
+ top: 48px;
3019
3030
  left: 0;
3020
3031
  right: 0;
3021
- bottom: 0;
3022
3032
  display: flex;
3023
3033
  align-items: center;
3024
3034
  justify-content: center;
3025
3035
  overflow: visible;
3036
+ height: calc(100vh - 48px);
3026
3037
  }
3027
3038
 
3028
3039
  .vuiImagePreview__image {
3029
3040
  max-width: calc(100vw - 64px);
3030
- max-height: calc(100vh - 64px);
3041
+ max-height: calc(100vh - 96px);
3031
3042
  width: auto;
3032
3043
  height: auto;
3033
3044
  object-fit: contain;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vectara/vectara-ui",
3
- "version": "9.16.1",
3
+ "version": "9.17.0",
4
4
  "homepage": "./",
5
5
  "description": "Vectara's design system, codified as a React and Sass component library",
6
6
  "author": "Vectara",