datastake-daf 0.6.210 → 0.6.211
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/components/index.js +150 -32
- package/package.json +1 -1
- package/src/@daf/core/components/Dashboard/Widget/ImageCarousel/components/CustomArrowButton/index.js +56 -40
- package/src/@daf/core/components/Dashboard/Widget/ImageCarousel/index.jsx +71 -4
- package/src/@daf/core/components/Dashboard/Widget/ImageCarousel/style.js +7 -4
- package/src/@daf/core/components/Dashboard/Widget/ImageCarousel/2ndCarousel.js +0 -185
package/dist/components/index.js
CHANGED
|
@@ -21250,13 +21250,91 @@ const CarouselWidget = /*#__PURE__*/React.forwardRef((_ref, ref) => {
|
|
|
21250
21250
|
});
|
|
21251
21251
|
CarouselWidget.displayName = 'CarouselWidget';
|
|
21252
21252
|
|
|
21253
|
-
|
|
21253
|
+
function CustomArrowButton({
|
|
21254
|
+
icon,
|
|
21255
|
+
onClick = () => {},
|
|
21256
|
+
isLeft = false,
|
|
21257
|
+
iconColor = "#666",
|
|
21258
|
+
hoverIconColor = "#1890ff"
|
|
21259
|
+
}) {
|
|
21260
|
+
const [isHovered, setIsHovered] = React.useState(false);
|
|
21261
|
+
|
|
21262
|
+
// Properly clone the icon and override color on hover
|
|
21263
|
+
const styledIcon = icon && /*#__PURE__*/React__default["default"].isValidElement(icon) ? /*#__PURE__*/React__default["default"].cloneElement(icon, {
|
|
21264
|
+
style: {
|
|
21265
|
+
...(icon.props.style || {}),
|
|
21266
|
+
color: isHovered ? hoverIconColor : iconColor,
|
|
21267
|
+
transition: "color 0.3s ease"
|
|
21268
|
+
}
|
|
21269
|
+
}) : icon;
|
|
21270
|
+
return /*#__PURE__*/jsxRuntime.jsx(antd.Button, {
|
|
21271
|
+
type: "default",
|
|
21272
|
+
shape: "circle",
|
|
21273
|
+
icon: styledIcon,
|
|
21274
|
+
onClick: onClick,
|
|
21275
|
+
style: {
|
|
21276
|
+
position: "absolute",
|
|
21277
|
+
right: !isLeft ? "40px" : "auto",
|
|
21278
|
+
left: isLeft ? "40px" : "auto",
|
|
21279
|
+
top: "58%",
|
|
21280
|
+
transform: "translateY(-50%)",
|
|
21281
|
+
zIndex: 10,
|
|
21282
|
+
backgroundColor: "rgba(255, 255, 255, 0.4)",
|
|
21283
|
+
border: "1px solid rgba(0, 0, 0, 0.1)",
|
|
21284
|
+
boxShadow: "0 2px 8px rgba(0, 0, 0, 0.2)",
|
|
21285
|
+
width: "40px",
|
|
21286
|
+
height: "40px",
|
|
21287
|
+
display: "flex",
|
|
21288
|
+
alignItems: "center",
|
|
21289
|
+
justifyContent: "center"
|
|
21290
|
+
},
|
|
21291
|
+
onMouseEnter: e => {
|
|
21292
|
+
e.currentTarget.style.backgroundColor = "rgba(255, 255, 255, 1)";
|
|
21293
|
+
setIsHovered(true);
|
|
21294
|
+
},
|
|
21295
|
+
onMouseLeave: e => {
|
|
21296
|
+
e.currentTarget.style.backgroundColor = "rgba(255, 255, 255, 0.4)";
|
|
21297
|
+
setIsHovered(false);
|
|
21298
|
+
}
|
|
21299
|
+
});
|
|
21300
|
+
}
|
|
21301
|
+
CustomArrowButton.propTypes = {
|
|
21302
|
+
icon: PropTypes__default["default"].node.isRequired,
|
|
21303
|
+
onClick: PropTypes__default["default"].func,
|
|
21304
|
+
isLeft: PropTypes__default["default"].bool,
|
|
21305
|
+
iconColor: PropTypes__default["default"].string,
|
|
21306
|
+
hoverIconColor: PropTypes__default["default"].string
|
|
21307
|
+
};
|
|
21308
|
+
|
|
21309
|
+
const StyledCarouselWrapper = dt.div`
|
|
21310
|
+
position: relative;
|
|
21311
|
+
|
|
21312
|
+
.ant-carousel .slick-dots li button {
|
|
21313
|
+
background: ${props => props.inactiveDotColor} !important;
|
|
21314
|
+
}
|
|
21315
|
+
|
|
21316
|
+
.ant-carousel .slick-dots li.slick-active button {
|
|
21317
|
+
background: ${props => props.activeDotColor} !important;
|
|
21318
|
+
}
|
|
21319
|
+
|
|
21320
|
+
.ant-carousel .slick-dots li.slick-active::after {
|
|
21321
|
+
background: ${props => props.activeDotColor} !important;
|
|
21322
|
+
}
|
|
21323
|
+
|
|
21324
|
+
`;
|
|
21325
|
+
|
|
21326
|
+
const _excluded$d = ["title", "images", "height", "fallback", "activeDotColor", "inactiveDotColor", "arrowIconColor", "arrowHoverIconColor", "customArrows"];
|
|
21254
21327
|
function ImageCarousel(_ref) {
|
|
21255
21328
|
let {
|
|
21256
21329
|
title,
|
|
21257
21330
|
images,
|
|
21258
21331
|
height = 400,
|
|
21259
|
-
fallback = "/assets/images/empty-box.svg"
|
|
21332
|
+
fallback = "/assets/images/empty-box.svg",
|
|
21333
|
+
activeDotColor = "#1890ff",
|
|
21334
|
+
inactiveDotColor = "rgba(255, 255, 255, 0.3)",
|
|
21335
|
+
arrowIconColor = "#666",
|
|
21336
|
+
arrowHoverIconColor = "#1890ff",
|
|
21337
|
+
customArrows = false
|
|
21260
21338
|
} = _ref,
|
|
21261
21339
|
rest = _objectWithoutProperties(_ref, _excluded$d);
|
|
21262
21340
|
const [previewVisible, setPreviewVisible] = React.useState(false);
|
|
@@ -21265,38 +21343,67 @@ function ImageCarousel(_ref) {
|
|
|
21265
21343
|
const handleCarouselChange = index => {
|
|
21266
21344
|
setCurrent(index);
|
|
21267
21345
|
};
|
|
21346
|
+
const goToPrevious = () => {
|
|
21347
|
+
var _carouselRef$current;
|
|
21348
|
+
(_carouselRef$current = carouselRef.current) === null || _carouselRef$current === void 0 || _carouselRef$current.prev();
|
|
21349
|
+
};
|
|
21350
|
+
const goToNext = () => {
|
|
21351
|
+
var _carouselRef$current2;
|
|
21352
|
+
(_carouselRef$current2 = carouselRef.current) === null || _carouselRef$current2 === void 0 || _carouselRef$current2.next();
|
|
21353
|
+
};
|
|
21268
21354
|
return /*#__PURE__*/jsxRuntime.jsxs(jsxRuntime.Fragment, {
|
|
21269
|
-
children: [/*#__PURE__*/jsxRuntime.
|
|
21270
|
-
|
|
21271
|
-
|
|
21272
|
-
|
|
21273
|
-
|
|
21355
|
+
children: [/*#__PURE__*/jsxRuntime.jsxs(StyledCarouselWrapper, {
|
|
21356
|
+
activeDotColor: activeDotColor,
|
|
21357
|
+
inactiveDotColor: inactiveDotColor,
|
|
21358
|
+
children: [/*#__PURE__*/jsxRuntime.jsx(CarouselWidget, {
|
|
21359
|
+
title: title,
|
|
21360
|
+
arrows: false,
|
|
21274
21361
|
ref: carouselRef,
|
|
21275
21362
|
afterChange: handleCarouselChange,
|
|
21276
21363
|
infinite: true,
|
|
21277
|
-
|
|
21278
|
-
|
|
21279
|
-
|
|
21280
|
-
|
|
21281
|
-
|
|
21282
|
-
|
|
21283
|
-
|
|
21284
|
-
|
|
21285
|
-
|
|
21286
|
-
|
|
21287
|
-
|
|
21288
|
-
|
|
21289
|
-
|
|
21290
|
-
|
|
21291
|
-
|
|
21292
|
-
|
|
21293
|
-
|
|
21294
|
-
|
|
21295
|
-
|
|
21296
|
-
|
|
21297
|
-
|
|
21298
|
-
|
|
21299
|
-
|
|
21364
|
+
dots: false,
|
|
21365
|
+
children: /*#__PURE__*/jsxRuntime.jsx(antd.Carousel, _objectSpread2(_objectSpread2({
|
|
21366
|
+
ref: carouselRef,
|
|
21367
|
+
afterChange: handleCarouselChange
|
|
21368
|
+
}, rest), {}, {
|
|
21369
|
+
infinite: true,
|
|
21370
|
+
children: images.map((image, index) => {
|
|
21371
|
+
const imageSrc = typeof image === "string" ? image : image.src;
|
|
21372
|
+
const imageAlt = typeof image === "string" ? "".concat(title, " - Image ").concat(index + 1) : image.alt;
|
|
21373
|
+
return /*#__PURE__*/jsxRuntime.jsx("div", {
|
|
21374
|
+
children: /*#__PURE__*/jsxRuntime.jsx(antd.Image, {
|
|
21375
|
+
src: imageSrc,
|
|
21376
|
+
alt: imageAlt,
|
|
21377
|
+
height: height,
|
|
21378
|
+
width: "100%",
|
|
21379
|
+
fallback: fallback,
|
|
21380
|
+
loading: "lazy",
|
|
21381
|
+
preview: {
|
|
21382
|
+
visible: false
|
|
21383
|
+
},
|
|
21384
|
+
onClick: () => {
|
|
21385
|
+
setCurrent(index);
|
|
21386
|
+
setPreviewVisible(true);
|
|
21387
|
+
}
|
|
21388
|
+
})
|
|
21389
|
+
}, imageSrc);
|
|
21390
|
+
})
|
|
21391
|
+
}))
|
|
21392
|
+
}), customArrows && images && images.length > 1 && /*#__PURE__*/jsxRuntime.jsxs(jsxRuntime.Fragment, {
|
|
21393
|
+
children: [/*#__PURE__*/jsxRuntime.jsx(CustomArrowButton, {
|
|
21394
|
+
icon: /*#__PURE__*/jsxRuntime.jsx(Icons.LeftOutlined, {}),
|
|
21395
|
+
onClick: goToPrevious,
|
|
21396
|
+
isLeft: true,
|
|
21397
|
+
iconColor: arrowIconColor,
|
|
21398
|
+
hoverIconColor: arrowHoverIconColor
|
|
21399
|
+
}), /*#__PURE__*/jsxRuntime.jsx(CustomArrowButton, {
|
|
21400
|
+
icon: /*#__PURE__*/jsxRuntime.jsx(Icons.RightOutlined, {}),
|
|
21401
|
+
onClick: goToNext,
|
|
21402
|
+
iconColor: arrowIconColor,
|
|
21403
|
+
hoverIconColor: arrowHoverIconColor
|
|
21404
|
+
})]
|
|
21405
|
+
})]
|
|
21406
|
+
}), /*#__PURE__*/jsxRuntime.jsx("div", {
|
|
21300
21407
|
style: {
|
|
21301
21408
|
display: "none"
|
|
21302
21409
|
},
|
|
@@ -21306,9 +21413,9 @@ function ImageCarousel(_ref) {
|
|
|
21306
21413
|
current,
|
|
21307
21414
|
onVisibleChange: vis => setPreviewVisible(vis),
|
|
21308
21415
|
onChange: idx => {
|
|
21309
|
-
var _carouselRef$
|
|
21416
|
+
var _carouselRef$current3;
|
|
21310
21417
|
setCurrent(idx);
|
|
21311
|
-
(_carouselRef$
|
|
21418
|
+
(_carouselRef$current3 = carouselRef.current) === null || _carouselRef$current3 === void 0 || _carouselRef$current3.goTo(idx);
|
|
21312
21419
|
}
|
|
21313
21420
|
},
|
|
21314
21421
|
children: images.map((image, index) => {
|
|
@@ -21323,6 +21430,17 @@ function ImageCarousel(_ref) {
|
|
|
21323
21430
|
})]
|
|
21324
21431
|
});
|
|
21325
21432
|
}
|
|
21433
|
+
ImageCarousel.propTypes = {
|
|
21434
|
+
title: PropTypes__default["default"].string.isRequired,
|
|
21435
|
+
images: PropTypes__default["default"].array.isRequired,
|
|
21436
|
+
height: PropTypes__default["default"].number,
|
|
21437
|
+
fallback: PropTypes__default["default"].string,
|
|
21438
|
+
activeDotColor: PropTypes__default["default"].string,
|
|
21439
|
+
inactiveDotColor: PropTypes__default["default"].string,
|
|
21440
|
+
arrowIconColor: PropTypes__default["default"].string,
|
|
21441
|
+
arrowHoverIconColor: PropTypes__default["default"].string,
|
|
21442
|
+
customArrows: PropTypes__default["default"].bool
|
|
21443
|
+
};
|
|
21326
21444
|
|
|
21327
21445
|
var Style$w = dt.div`
|
|
21328
21446
|
border: 1px solid var(--base-gray-40);
|
package/package.json
CHANGED
|
@@ -1,52 +1,68 @@
|
|
|
1
|
+
import React, { useState } from "react";
|
|
1
2
|
import { Button } from "antd";
|
|
2
|
-
import
|
|
3
|
+
import PropTypes from "prop-types";
|
|
3
4
|
|
|
4
5
|
function CustomArrowButton({
|
|
5
|
-
icon,
|
|
6
|
-
onClick = () => {},
|
|
7
|
-
isLeft = false,
|
|
8
|
-
iconColor = "#666",
|
|
9
|
-
hoverIconColor = "#1890ff"
|
|
6
|
+
icon,
|
|
7
|
+
onClick = () => {},
|
|
8
|
+
isLeft = false,
|
|
9
|
+
iconColor = "#666",
|
|
10
|
+
hoverIconColor = "#1890ff",
|
|
10
11
|
}) {
|
|
11
12
|
const [isHovered, setIsHovered] = useState(false);
|
|
12
13
|
|
|
13
|
-
//
|
|
14
|
-
const styledIcon =
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
// Properly clone the icon and override color on hover
|
|
15
|
+
const styledIcon =
|
|
16
|
+
icon && React.isValidElement(icon)
|
|
17
|
+
? React.cloneElement(icon, {
|
|
18
|
+
style: {
|
|
19
|
+
...(icon.props.style || {}),
|
|
20
|
+
color: isHovered ? hoverIconColor : iconColor,
|
|
21
|
+
transition: "color 0.3s ease",
|
|
22
|
+
},
|
|
23
|
+
})
|
|
24
|
+
: icon;
|
|
17
25
|
|
|
18
26
|
return (
|
|
19
27
|
<Button
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
28
|
+
type="default"
|
|
29
|
+
shape="circle"
|
|
30
|
+
icon={styledIcon}
|
|
31
|
+
onClick={onClick}
|
|
32
|
+
style={{
|
|
33
|
+
position: "absolute",
|
|
34
|
+
right: !isLeft ? "40px" : "auto",
|
|
35
|
+
left: isLeft ? "40px" : "auto",
|
|
36
|
+
top: "58%",
|
|
37
|
+
transform: "translateY(-50%)",
|
|
38
|
+
zIndex: 10,
|
|
39
|
+
backgroundColor: "rgba(255, 255, 255, 0.4)",
|
|
40
|
+
border: "1px solid rgba(0, 0, 0, 0.1)",
|
|
41
|
+
boxShadow: "0 2px 8px rgba(0, 0, 0, 0.2)",
|
|
42
|
+
width: "40px",
|
|
43
|
+
height: "40px",
|
|
44
|
+
display: "flex",
|
|
45
|
+
alignItems: "center",
|
|
46
|
+
justifyContent: "center",
|
|
47
|
+
}}
|
|
48
|
+
onMouseEnter={(e) => {
|
|
49
|
+
e.currentTarget.style.backgroundColor = "rgba(255, 255, 255, 1)";
|
|
50
|
+
setIsHovered(true);
|
|
51
|
+
}}
|
|
52
|
+
onMouseLeave={(e) => {
|
|
53
|
+
e.currentTarget.style.backgroundColor = "rgba(255, 255, 255, 0.4)";
|
|
54
|
+
setIsHovered(false);
|
|
55
|
+
}}
|
|
48
56
|
/>
|
|
49
|
-
)
|
|
57
|
+
);
|
|
50
58
|
}
|
|
51
59
|
|
|
52
|
-
|
|
60
|
+
CustomArrowButton.propTypes = {
|
|
61
|
+
icon: PropTypes.node.isRequired,
|
|
62
|
+
onClick: PropTypes.func,
|
|
63
|
+
isLeft: PropTypes.bool,
|
|
64
|
+
iconColor: PropTypes.string,
|
|
65
|
+
hoverIconColor: PropTypes.string,
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export default CustomArrowButton;
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import React, { useState, useRef } from "react";
|
|
2
2
|
import CarouselWidget from "../CarouselWidget/index.jsx";
|
|
3
3
|
import { Image, Carousel } from "antd";
|
|
4
|
+
import { LeftOutlined, RightOutlined } from "@ant-design/icons";
|
|
5
|
+
import CustomArrowButton from "./components/CustomArrowButton/index.js";
|
|
6
|
+
import { StyledCarouselWrapper } from "./style.js";
|
|
7
|
+
import PropTypes from "prop-types";
|
|
4
8
|
|
|
5
9
|
/**
|
|
6
10
|
* @typedef {Object} ImageObject
|
|
@@ -36,6 +40,11 @@ import { Image, Carousel } from "antd";
|
|
|
36
40
|
* @param {(string|ImageObject)[]} props.images - Array of image URLs (strings) or image objects with src and alt properties
|
|
37
41
|
* @param {number} [props.height=400] - Height of the carousel images in pixels
|
|
38
42
|
* @param {string} [props.fallback="/assets/images/empty-box.svg"] - Fallback image URL when original image fails to load
|
|
43
|
+
* @param {string} [props.activeDotColor="#1890ff"] - Color of the active dot
|
|
44
|
+
* @param {string} [props.inactiveDotColor="rgba(255, 255, 255, 0.3)"] - Color of inactive dots
|
|
45
|
+
* @param {string} [props.arrowIconColor="#666"] - Default color of arrow icons
|
|
46
|
+
* @param {string} [props.arrowHoverIconColor="#1890ff"] - Color of arrow icons on hover
|
|
47
|
+
* @param {boolean} [props.customArrows=false] - Whether to show custom navigation arrows
|
|
39
48
|
* @param {Object} [props.rest] - Additional props passed to the underlying CarouselWidget component
|
|
40
49
|
*
|
|
41
50
|
* @features
|
|
@@ -46,6 +55,8 @@ import { Image, Carousel } from "antd";
|
|
|
46
55
|
* - ✅ Support for both string URLs and image objects with alt text
|
|
47
56
|
* - ✅ Synchronized carousel and preview navigation
|
|
48
57
|
* - ✅ Infinite scrolling in both carousel and preview modes
|
|
58
|
+
* - ✅ Placeholder image for empty image arrays (/assets/images/no-image.svg)
|
|
59
|
+
* - ✅ Customizable dot colors via props
|
|
49
60
|
*
|
|
50
61
|
* @accessibility
|
|
51
62
|
* - Proper alt text support for screen readers
|
|
@@ -58,6 +69,11 @@ function ImageCarousel({
|
|
|
58
69
|
images,
|
|
59
70
|
height = 400,
|
|
60
71
|
fallback = "/assets/images/empty-box.svg",
|
|
72
|
+
activeDotColor = "#1890ff",
|
|
73
|
+
inactiveDotColor = "rgba(255, 255, 255, 0.3)",
|
|
74
|
+
arrowIconColor = "#666",
|
|
75
|
+
arrowHoverIconColor = "#1890ff",
|
|
76
|
+
customArrows = false,
|
|
61
77
|
...rest
|
|
62
78
|
}) {
|
|
63
79
|
const [previewVisible, setPreviewVisible] = useState(false);
|
|
@@ -68,10 +84,30 @@ function ImageCarousel({
|
|
|
68
84
|
setCurrent(index);
|
|
69
85
|
};
|
|
70
86
|
|
|
87
|
+
const goToPrevious = () => {
|
|
88
|
+
carouselRef.current?.prev();
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const goToNext = () => {
|
|
92
|
+
carouselRef.current?.next();
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
|
|
71
96
|
return (
|
|
72
97
|
<>
|
|
73
|
-
<
|
|
74
|
-
|
|
98
|
+
<StyledCarouselWrapper
|
|
99
|
+
activeDotColor={activeDotColor}
|
|
100
|
+
inactiveDotColor={inactiveDotColor}
|
|
101
|
+
>
|
|
102
|
+
<CarouselWidget
|
|
103
|
+
title={title}
|
|
104
|
+
arrows={false}
|
|
105
|
+
ref={carouselRef}
|
|
106
|
+
afterChange={handleCarouselChange}
|
|
107
|
+
infinite
|
|
108
|
+
dots={false}
|
|
109
|
+
>
|
|
110
|
+
<Carousel ref={carouselRef} afterChange={handleCarouselChange} {...rest} infinite>
|
|
75
111
|
{images.map((image, index) => {
|
|
76
112
|
const imageSrc = typeof image === "string" ? image : image.src;
|
|
77
113
|
const imageAlt =
|
|
@@ -100,7 +136,26 @@ function ImageCarousel({
|
|
|
100
136
|
);
|
|
101
137
|
})}
|
|
102
138
|
</Carousel>
|
|
103
|
-
|
|
139
|
+
</CarouselWidget>
|
|
140
|
+
|
|
141
|
+
{customArrows && images && images.length > 1 && (
|
|
142
|
+
<>
|
|
143
|
+
<CustomArrowButton
|
|
144
|
+
icon={<LeftOutlined />}
|
|
145
|
+
onClick={goToPrevious}
|
|
146
|
+
isLeft={true}
|
|
147
|
+
iconColor={arrowIconColor}
|
|
148
|
+
hoverIconColor={arrowHoverIconColor}
|
|
149
|
+
/>
|
|
150
|
+
<CustomArrowButton
|
|
151
|
+
icon={<RightOutlined />}
|
|
152
|
+
onClick={goToNext}
|
|
153
|
+
iconColor={arrowIconColor}
|
|
154
|
+
hoverIconColor={arrowHoverIconColor}
|
|
155
|
+
/>
|
|
156
|
+
</>
|
|
157
|
+
)}
|
|
158
|
+
</StyledCarouselWrapper>
|
|
104
159
|
|
|
105
160
|
<div style={{ display: "none" }}>
|
|
106
161
|
<Image.PreviewGroup
|
|
@@ -129,4 +184,16 @@ function ImageCarousel({
|
|
|
129
184
|
);
|
|
130
185
|
}
|
|
131
186
|
|
|
132
|
-
|
|
187
|
+
ImageCarousel.propTypes = {
|
|
188
|
+
title: PropTypes.string.isRequired,
|
|
189
|
+
images: PropTypes.array.isRequired,
|
|
190
|
+
height: PropTypes.number,
|
|
191
|
+
fallback: PropTypes.string,
|
|
192
|
+
activeDotColor: PropTypes.string,
|
|
193
|
+
inactiveDotColor: PropTypes.string,
|
|
194
|
+
arrowIconColor: PropTypes.string,
|
|
195
|
+
arrowHoverIconColor: PropTypes.string,
|
|
196
|
+
customArrows: PropTypes.bool,
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
export default ImageCarousel;
|
|
@@ -4,12 +4,15 @@ export const StyledCarouselWrapper = styled.div`
|
|
|
4
4
|
position: relative;
|
|
5
5
|
|
|
6
6
|
.ant-carousel .slick-dots li button {
|
|
7
|
-
background: ${props => props.inactiveDotColor} !important;
|
|
8
|
-
opacity: 1 !important;
|
|
7
|
+
background: ${props => props.inactiveDotColor} !important;
|
|
9
8
|
}
|
|
10
|
-
|
|
9
|
+
|
|
11
10
|
.ant-carousel .slick-dots li.slick-active button {
|
|
11
|
+
background: ${props => props.activeDotColor} !important;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.ant-carousel .slick-dots li.slick-active::after {
|
|
12
15
|
background: ${props => props.activeDotColor} !important;
|
|
13
|
-
opacity: 1 !important;
|
|
14
16
|
}
|
|
17
|
+
|
|
15
18
|
`;
|
|
@@ -1,185 +0,0 @@
|
|
|
1
|
-
import React, { useState, useRef } from "react";
|
|
2
|
-
import styled from "styled-components";
|
|
3
|
-
import CarouselWidget from "../CarouselWidget/index.jsx";
|
|
4
|
-
import { Image, Carousel, Button } from "antd";
|
|
5
|
-
import { LeftOutlined, RightOutlined } from "@ant-design/icons";
|
|
6
|
-
import CustomArrowButton from "./components/CustomArrowButton/index.js";
|
|
7
|
-
import { StyledCarouselWrapper } from "./style.js";
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* @typedef {Object} ImageObject
|
|
11
|
-
* @property {string} src - The source URL of the image
|
|
12
|
-
* @property {string} alt - Alternative text for the image
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* ImageCarousel Component - A responsive image carousel with preview functionality and keyboard navigation
|
|
17
|
-
*
|
|
18
|
-
* @component
|
|
19
|
-
* @example
|
|
20
|
-
* // Basic usage with string URLs
|
|
21
|
-
* <ImageCarousel
|
|
22
|
-
* title="My Gallery"
|
|
23
|
-
* images={["image1.jpg", "image2.jpg"]}
|
|
24
|
-
* />
|
|
25
|
-
*
|
|
26
|
-
* @example
|
|
27
|
-
* // Advanced usage with image objects and custom height
|
|
28
|
-
* <ImageCarousel
|
|
29
|
-
* title="Nature Gallery"
|
|
30
|
-
* images={[
|
|
31
|
-
* { src: "landscape.jpg", alt: "Beautiful mountain landscape" },
|
|
32
|
-
* { src: "forest.jpg", alt: "Dense forest path" }
|
|
33
|
-
* ]}
|
|
34
|
-
* height={300}
|
|
35
|
-
* fallback="/custom-fallback.svg"
|
|
36
|
-
* />
|
|
37
|
-
*
|
|
38
|
-
* @param {Object} props - Component props
|
|
39
|
-
* @param {string} props.title - The title displayed in the widget header
|
|
40
|
-
* @param {(string|ImageObject)[]} props.images - Array of image URLs (strings) or image objects with src and alt properties
|
|
41
|
-
* @param {number} [props.height=400] - Height of the carousel images in pixels
|
|
42
|
-
* @param {string} [props.fallback="/assets/images/empty-box.svg"] - Fallback image URL when original image fails to load
|
|
43
|
-
* @param {string} [props.activeDotColor="#1890ff"] - Color of the active dot
|
|
44
|
-
* @param {string} [props.inactiveDotColor="rgba(255, 255, 255, 0.3)"] - Color of inactive dots
|
|
45
|
-
* @param {string} [props.arrowIconColor="#666"] - Default color of arrow icons
|
|
46
|
-
* @param {string} [props.arrowHoverIconColor="#1890ff"] - Color of arrow icons on hover
|
|
47
|
-
* @param {boolean} [props.customArrows=false] - Whether to show custom navigation arrows
|
|
48
|
-
* @param {Object} [props.rest] - Additional props passed to the underlying CarouselWidget component
|
|
49
|
-
*
|
|
50
|
-
* @features
|
|
51
|
-
* - ✅ Responsive image display with lazy loading
|
|
52
|
-
* - ✅ Click-to-preview with full-screen image viewer
|
|
53
|
-
* - ✅ Keyboard navigation in preview mode (← → arrow keys)
|
|
54
|
-
* - ✅ Automatic fallback for broken images
|
|
55
|
-
* - ✅ Support for both string URLs and image objects with alt text
|
|
56
|
-
* - ✅ Synchronized carousel and preview navigation
|
|
57
|
-
* - ✅ Infinite scrolling in both carousel and preview modes
|
|
58
|
-
* - ✅ Placeholder image for empty image arrays (/assets/images/no-image.svg)
|
|
59
|
-
* - ✅ Customizable dot colors via props
|
|
60
|
-
*
|
|
61
|
-
* @accessibility
|
|
62
|
-
* - Proper alt text support for screen readers
|
|
63
|
-
* - Keyboard navigation support
|
|
64
|
-
* - Focus management in preview mode
|
|
65
|
-
*
|
|
66
|
-
*/
|
|
67
|
-
function ImageCarousel({
|
|
68
|
-
title,
|
|
69
|
-
images,
|
|
70
|
-
height = 400,
|
|
71
|
-
fallback = "/assets/images/empty-box.svg",
|
|
72
|
-
activeDotColor = "#1890ff",
|
|
73
|
-
inactiveDotColor = "rgba(255, 255, 255, 0.3)",
|
|
74
|
-
arrowIconColor = "#666",
|
|
75
|
-
arrowHoverIconColor = "#1890ff",
|
|
76
|
-
customArrows = false,
|
|
77
|
-
...rest
|
|
78
|
-
}) {
|
|
79
|
-
const [previewVisible, setPreviewVisible] = useState(false);
|
|
80
|
-
const [current, setCurrent] = useState(0);
|
|
81
|
-
const carouselRef = useRef(null);
|
|
82
|
-
|
|
83
|
-
const handleCarouselChange = (index) => {
|
|
84
|
-
setCurrent(index);
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
const goToPrevious = () => {
|
|
88
|
-
carouselRef.current?.prev();
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
const goToNext = () => {
|
|
92
|
-
carouselRef.current?.next();
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
return (
|
|
97
|
-
<>
|
|
98
|
-
{/* <StyledCarouselWrapper
|
|
99
|
-
activeDotColor={activeDotColor}
|
|
100
|
-
inactiveDotColor={inactiveDotColor}
|
|
101
|
-
> */}
|
|
102
|
-
<CarouselWidget
|
|
103
|
-
title={title}
|
|
104
|
-
{...rest}
|
|
105
|
-
arrows={false}
|
|
106
|
-
ref={carouselRef}
|
|
107
|
-
afterChange={handleCarouselChange}
|
|
108
|
-
infinite
|
|
109
|
-
>
|
|
110
|
-
{images.map((image, index) => {
|
|
111
|
-
const imageSrc = typeof image === "string" ? image : image.src;
|
|
112
|
-
const imageAlt =
|
|
113
|
-
typeof image === "string"
|
|
114
|
-
? `${title} - Image ${index + 1}`
|
|
115
|
-
: image.alt;
|
|
116
|
-
|
|
117
|
-
return (
|
|
118
|
-
<div key={imageSrc}>
|
|
119
|
-
<Image
|
|
120
|
-
src={imageSrc}
|
|
121
|
-
alt={imageAlt}
|
|
122
|
-
height={height}
|
|
123
|
-
width="100%"
|
|
124
|
-
fallback={fallback}
|
|
125
|
-
loading="lazy"
|
|
126
|
-
preview={{
|
|
127
|
-
visible: false,
|
|
128
|
-
}}
|
|
129
|
-
onClick={() => {
|
|
130
|
-
setCurrent(index);
|
|
131
|
-
setPreviewVisible(true);
|
|
132
|
-
}}
|
|
133
|
-
/>
|
|
134
|
-
</div>
|
|
135
|
-
);
|
|
136
|
-
})}
|
|
137
|
-
</CarouselWidget>
|
|
138
|
-
|
|
139
|
-
{customArrows && images && images.length > 1 && (
|
|
140
|
-
<>
|
|
141
|
-
<CustomArrowButton
|
|
142
|
-
icon={<LeftOutlined />}
|
|
143
|
-
onClick={goToPrevious}
|
|
144
|
-
isLeft={true}
|
|
145
|
-
iconColor={arrowIconColor}
|
|
146
|
-
hoverIconColor={arrowHoverIconColor}
|
|
147
|
-
/>
|
|
148
|
-
<CustomArrowButton
|
|
149
|
-
icon={<RightOutlined />}
|
|
150
|
-
onClick={goToNext}
|
|
151
|
-
iconColor={arrowIconColor}
|
|
152
|
-
hoverIconColor={arrowHoverIconColor}
|
|
153
|
-
/>
|
|
154
|
-
</>
|
|
155
|
-
)}
|
|
156
|
-
{/* </StyledCarouselWrapper> */}
|
|
157
|
-
|
|
158
|
-
<div style={{ display: "none" }}>
|
|
159
|
-
<Image.PreviewGroup
|
|
160
|
-
preview={{
|
|
161
|
-
visible: previewVisible,
|
|
162
|
-
current,
|
|
163
|
-
onVisibleChange: (vis) => setPreviewVisible(vis),
|
|
164
|
-
onChange: (idx) => {
|
|
165
|
-
setCurrent(idx);
|
|
166
|
-
carouselRef.current?.goTo(idx);
|
|
167
|
-
},
|
|
168
|
-
}}
|
|
169
|
-
>
|
|
170
|
-
{images.map((image, index) => {
|
|
171
|
-
const imageSrc = typeof image === "string" ? image : image.src;
|
|
172
|
-
const imageAlt =
|
|
173
|
-
typeof image === "string"
|
|
174
|
-
? `${title} - Image ${index + 1}`
|
|
175
|
-
: image.alt;
|
|
176
|
-
|
|
177
|
-
return <Image key={imageSrc} src={imageSrc} alt={imageAlt} />;
|
|
178
|
-
})}
|
|
179
|
-
</Image.PreviewGroup>
|
|
180
|
-
</div>
|
|
181
|
-
</>
|
|
182
|
-
);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
export default ImageCarousel;
|