@yogiswara/honcho-editor-ui 2.0.7 → 2.1.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.
@@ -0,0 +1,9 @@
1
+ export declare const neutral: {
2
+ white: string;
3
+ 10: string;
4
+ 25: string;
5
+ 50: string;
6
+ 75: string;
7
+ 100: string;
8
+ 200: string;
9
+ };
package/dist/color.js ADDED
@@ -0,0 +1,9 @@
1
+ export const neutral = {
2
+ white: "#FFFFFF",
3
+ 10: "#F9F9F9",
4
+ 25: "#DBDBDB",
5
+ 50: "#8D8D8D",
6
+ 75: "#575757",
7
+ 100: "#0A0A1C",
8
+ 200: "#000000",
9
+ };
@@ -0,0 +1,18 @@
1
+ import React from "react";
2
+ import { GallerySetup } from "../../../hooks/editor/type";
3
+ import { AdjustmentValues } from "../../../lib/editor/honcho-editor";
4
+ interface PhotoData extends GallerySetup {
5
+ adjustments?: Partial<AdjustmentValues>;
6
+ frame?: string;
7
+ }
8
+ interface ImageGalleryProps {
9
+ imageCollection: PhotoData[];
10
+ isSelectedMode: boolean;
11
+ isHiddenGallery: boolean;
12
+ enableEditor: boolean;
13
+ onPreview: (photo: PhotoData) => () => void;
14
+ onSelectedMode: () => void;
15
+ onToggleSelect: (photo: PhotoData) => () => void;
16
+ }
17
+ declare const AlbumImageGallery: React.FC<ImageGalleryProps>;
18
+ export default AlbumImageGallery;
@@ -1,52 +1,32 @@
1
- "use strict";
2
- // "use client";
3
- // import React from "react";
4
- // import { Box } from "@mui/material";
5
- // import Masonry, { ResponsiveMasonry } from "react-responsive-masonry";
6
- // import ImageItem from "../GalleryAlbum/ImageItem";
7
- // import { GallerySetup } from "../../../hooks/editor/type";
8
- // interface ImageGalleryProps {
9
- // imageCollection: GallerySetup[];
10
- // isSelectedMode: boolean;
11
- // isHiddenGallery: boolean;
12
- // onPreview: (photo: unknown) => () => void;
13
- // onSelectedMode: () => void;
14
- // onToggleSelect: (photo: unknown) => () => void;
15
- // }
16
- // const AlbumImageGallery: React.FC<ImageGalleryProps> = (props) => {
17
- // const {
18
- // imageCollection,
19
- // isSelectedMode,
20
- // isHiddenGallery,
21
- // onPreview,
22
- // onSelectedMode,
23
- // onToggleSelect,
24
- // } = props;
25
- // return (
26
- // <section>
27
- // <ResponsiveMasonry columnsCountBreakPoints={{ 750: 2, 900: 4 }}>
28
- // <Masonry>
29
- // {imageCollection.map((photo, index) => (
30
- // <Box key={photo.key} sx={{ m: 0.5 }}>
31
- // <ImageItem
32
- // margin="0px"
33
- // index={index}
34
- // photo={photo}
35
- // direction="column"
36
- // isFullScreenMode={false}
37
- // isSelected={photo.isSelected}
38
- // isSelectedMode={isSelectedMode}
39
- // isHiddenGallery={isHiddenGallery}
40
- // onPreview={onPreview(photo)}
41
- // onSelectedMode={onSelectedMode}
42
- // onToggleSelect={onToggleSelect(photo)}
43
- // frame={photo.frame}
44
- // />
45
- // </Box>
46
- // ))}
47
- // </Masonry>
48
- // </ResponsiveMasonry>
49
- // </section>
50
- // );
51
- // };
52
- // export default AlbumImageGallery;
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { Box } from "@mui/material";
4
+ import Masonry, { ResponsiveMasonry } from "react-responsive-masonry";
5
+ import GalleryImageItem from "./ImageItem";
6
+ const AlbumImageGallery = (props) => {
7
+ const { imageCollection, isSelectedMode, isHiddenGallery, enableEditor, // Destructure the new prop
8
+ onPreview, onSelectedMode, onToggleSelect, } = props;
9
+ return (_jsx("section", { children: _jsx(ResponsiveMasonry, { columnsCountBreakPoints: { 750: 2, 900: 4 }, children: _jsx(Masonry, { children: imageCollection.map((photo, index) => {
10
+ // This guard clause is still important for runtime safety.
11
+ if (!photo.key || !photo.src) {
12
+ console.warn("Skipping item without a key or src:", photo);
13
+ return null;
14
+ }
15
+ // NEW: Create a new object that matches the 'PhotoProps' interface.
16
+ // This explicitly tells TypeScript that all required fields are present.
17
+ const imageItemPhotoProps = {
18
+ key: photo.key,
19
+ src: photo.src,
20
+ width: photo.width,
21
+ height: photo.height,
22
+ alt: photo.alt,
23
+ // We pass the original photo object in the generic 'photo' property
24
+ // in case ImageItem needs it for other operations.
25
+ photo: photo,
26
+ };
27
+ return (_jsx(Box, { sx: { m: 0.5 }, children: _jsx(GalleryImageItem, { margin: "0px", index: index,
28
+ // UPDATED: Pass the new, correctly-typed object.
29
+ photo: imageItemPhotoProps, direction: "column", isFullScreenMode: false, isSelected: photo.isSelected, isSelectedMode: isSelectedMode, isHiddenGallery: isHiddenGallery, onPreview: onPreview(photo), onSelectedMode: onSelectedMode, onToggleSelect: onToggleSelect(photo), enableEditor: enableEditor, adjustments: photo.adjustments, frame: photo.frame }) }, photo.key));
30
+ }) }) }) }));
31
+ };
32
+ export default AlbumImageGallery;
@@ -0,0 +1,28 @@
1
+ import type { AdjustmentValues } from "../../../lib/editor/honcho-editor";
2
+ import { GallerySetup } from "../../../hooks/editor/type";
3
+ interface PhotoProps<T> {
4
+ src: string;
5
+ alt?: string;
6
+ width: number;
7
+ height: number;
8
+ key: string;
9
+ photo?: T;
10
+ }
11
+ interface Props {
12
+ margin?: any;
13
+ index: number;
14
+ photo: PhotoProps<GallerySetup>;
15
+ direction: "row" | "column";
16
+ isSelectedMode: boolean;
17
+ isFullScreenMode: boolean;
18
+ isSelected?: boolean;
19
+ isHiddenGallery: boolean;
20
+ onToggleSelect: () => void;
21
+ onPreview: () => void;
22
+ onSelectedMode: () => void;
23
+ enableEditor?: boolean;
24
+ adjustments?: Partial<AdjustmentValues>;
25
+ frame?: string | null;
26
+ }
27
+ declare const GalleryImageItem: (props: Props) => import("react/jsx-runtime").JSX.Element;
28
+ export default GalleryImageItem;
@@ -1,246 +1,165 @@
1
- "use strict";
2
- // import { CSSProperties, useMemo } from "react";
3
- // import { Box, CardMedia, useTheme } from "@mui/material";
4
- // import { TickCircle } from "iconsax-react";
5
- // import CustomTickIcon from "@/assets/svg/Tick";
6
- // import { useImageProcessor } from "@/lib/hooks/useImageProcessor";
7
- // import type { AdjustmentValues } from "@/lib/honcho-editor";
8
- // import { GallerySetup } from "../../../hooks/editor/type";
9
- // import { neutral } from "@/color";
10
- // import { log } from "@/utils/logger";
11
- // const imgStyle = {
12
- // transition: "transform .135s cubic-bezier(0.0,0.0,0.2,1),opacity linear .15s",
13
- // width: "100%",
14
- // // objectFit: "contain",
15
- // //height: "100%",
16
- // };
17
- // const selectedImgStyle = {
18
- // borderRadius: "8px",
19
- // transform: "translateZ(0px) scale3d(1, 1, 1)",
20
- // // transition: "transform .135s cubic-bezier(0.0,0.0,0.2,1),opacity linear .15s"
21
- // };
22
- // interface PhotoProps<T> {
23
- // src: string;
24
- // alt: string;
25
- // width: number;
26
- // height: number;
27
- // key: string;
28
- // // other properties
29
- // photo?: T;
30
- // }
31
- // interface Props {
32
- // margin?: any;
33
- // index: number;
34
- // photo: PhotoProps<GallerySetup>;
35
- // // onClick: renderImageClickHandler | null
36
- // direction: "row" | "column";
37
- // isSelectedMode: boolean;
38
- // isFullScreenMode: boolean; // this preview Mode we plan to use this component on there
39
- // isSelected: boolean;
40
- // isHiddenGallery: boolean;
41
- // onToggleSelect: () => void;
42
- // onPreview: () => void;
43
- // onSelectedMode: () => void;
44
- // // NEW: Editor interceptor props
45
- // enableEditor?: boolean;
46
- // adjustments?: Partial<AdjustmentValues>;
47
- // frame?: string | null;
48
- // }
49
- // const ImageItem = (props: Props) => {
50
- // const { photo, margin, enableEditor = true, adjustments, frame } = props;
51
- // const theme = useTheme();
52
- // // Memoize adjustments to prevent infinite re-renders
53
- // const memoizedAdjustments = useMemo(() => ({
54
- // ...adjustments,
55
- // exposure: 50,
56
- // }), [adjustments]);
57
- // // Use the image processor hook
58
- // const { processedImageSrc, isProcessingComplete } = useImageProcessor({
59
- // photoId: photo.key,
60
- // photoSrc: photo.src,
61
- // enableEditor,
62
- // adjustments: memoizedAdjustments,
63
- // frame
64
- // });
65
- // const styleHiddenGallery: CSSProperties = useMemo(() => {
66
- // if (props.isHiddenGallery) {
67
- // return { filter: "blur(20px)", pointerEvents: "none" };
68
- // } else {
69
- // return {};
70
- // }
71
- // }, [props.isHiddenGallery]);
72
- // const commonStyle = useMemo(() => {
73
- // return {
74
- // backgroundColor: neutral.white,
75
- // overflow: "hidden",
76
- // position: "relative",
77
- // width: "100%",
78
- // aspectRatio: `${photo.width}/${photo.height}`,
79
- // };
80
- // }, [props.direction, props.photo]);
81
- // const handleImageClick = () => {
82
- // log.debug(
83
- // {
84
- // isFullMode: props.isFullScreenMode,
85
- // isShowGallery: props.isHiddenGallery,
86
- // },
87
- // "handleImageClick"
88
- // );
89
- // if (!props.isFullScreenMode && !props.isHiddenGallery) {
90
- // if (props.isSelectedMode) {
91
- // log.debug("handleImageClick with toggle select");
92
- // props.onToggleSelect();
93
- // } else {
94
- // log.debug("handleImageClick with preview");
95
- // props.onPreview();
96
- // }
97
- // }
98
- // };
99
- // const handleImageSelectedIconClick = () => {
100
- // log.debug("handleImageSelectedIconClick");
101
- // if (!props.isFullScreenMode) {
102
- // if (!props.isSelectedMode) {
103
- // props.onSelectedMode();
104
- // }
105
- // props.onToggleSelect();
106
- // }
107
- // };
108
- // const imageSx = useMemo(() => {
109
- // const baseStyles = props.isSelected ? {
110
- // ...imgStyle,
111
- // ...selectedImgStyle,
112
- // ...styleHiddenGallery,
113
- // aspectRatio: `${photo.width}/${photo.height}`,
114
- // } : {
115
- // ...imgStyle,
116
- // ...styleHiddenGallery,
117
- // };
118
- // return {
119
- // ...baseStyles,
120
- // opacity: isProcessingComplete ? 1 : 0,
121
- // transition: 'opacity 0.3s ease-in-out',
122
- // };
123
- // }, [props.isSelected, styleHiddenGallery, photo.width, photo.height, isProcessingComplete]);
124
- // const boxNotSelected = useMemo(
125
- // () => ({
126
- // margin,
127
- // // height: photo.height,
128
- // width: "100%",
129
- // ...commonStyle,
130
- // transition: ".3s",
131
- // // "&:hover": { padding: "12px", backgroundColor: "primary.light1" },
132
- // "&:-webkit-transition": { transition: ".3s" },
133
- // "&:hover > .checkbox": { display: props.isHiddenGallery ? "" : "block" },
134
- // cursor:
135
- // props.isFullScreenMode || props.isHiddenGallery ? "inherit" : "pointer",
136
- // backgroundColor: props.isFullScreenMode
137
- // ? "rgba(0,0,0,0.1)"
138
- // : "transparent",
139
- // }),
140
- // [margin, commonStyle, props.isHiddenGallery, props.isFullScreenMode]
141
- // );
142
- // const boxSelected = useMemo(
143
- // () => ({
144
- // margin,
145
- // // height: photo.height,
146
- // width: "100%",
147
- // ...commonStyle,
148
- // cursor:
149
- // props.isFullScreenMode || props.isHiddenGallery ? "inherit" : "pointer",
150
- // transition: ".3s",
151
- // "&:-webkit-transition": { transition: ".3s" },
152
- // padding: { xs: "13px 12px", sm: "21.31px 25.56px 21.32px 27.68px" },
153
- // backgroundColor: theme.palette.light["Surface-Variant-2"],
154
- // }),
155
- // [
156
- // margin,
157
- // commonStyle,
158
- // theme.palette.light,
159
- // props.isFullScreenMode,
160
- // props.isHiddenGallery,
161
- // ]
162
- // );
163
- // const boxOuterSx = useMemo(() => {
164
- // if (props.isFullScreenMode) {
165
- // return {
166
- // margin,
167
- // width: "100%",
168
- // ...commonStyle,
169
- // };
170
- // } else {
171
- // if (props.isSelected) {
172
- // return { ...boxSelected };
173
- // } else {
174
- // return { ...boxNotSelected };
175
- // }
176
- // }
177
- // }, [
178
- // props.isFullScreenMode,
179
- // photo.height,
180
- // photo.width,
181
- // commonStyle,
182
- // boxSelected,
183
- // boxNotSelected,
184
- // margin,
185
- // props.isSelected,
186
- // ]);
187
- // return (
188
- // <Box id={"Box_image"} key={photo.key} sx={boxOuterSx} className={"image"}>
189
- // {!props.isHiddenGallery &&
190
- // (props.isSelected ? (
191
- // <Box
192
- // color={"primary.dark1"}
193
- // onClick={handleImageSelectedIconClick}
194
- // sx={{
195
- // position: "absolute",
196
- // width: "19px",
197
- // height: "19px",
198
- // zIndex: "2",
199
- // left: "5px",
200
- // top: "5px",
201
- // borderRadius: { xs: "50%", sm: 0 },
202
- // }}
203
- // className={"checkbox"}
204
- // >
205
- // <CustomTickIcon />
206
- // </Box>
207
- // ) : (
208
- // <Box
209
- // color={"neutral.light2"}
210
- // onClick={handleImageSelectedIconClick}
211
- // sx={{
212
- // position: "absolute",
213
- // width: "19px",
214
- // height: "19px",
215
- // zIndex: "1",
216
- // left: "5px",
217
- // top: "5px",
218
- // display: "none",
219
- // visibility: {
220
- // xs: props.isSelectedMode ? "visible" : "hidden",
221
- // sm: "visible",
222
- // },
223
- // }}
224
- // className={"checkbox"}
225
- // >
226
- // <TickCircle
227
- // style={{ width: "24px", height: "24px" }}
228
- // color="white"
229
- // />
230
- // </Box>
231
- // ))}
232
- // <CardMedia
233
- // id="card_media"
234
- // component="img"
235
- // className="image"
236
- // loading="lazy"
237
- // alt={photo.alt}
238
- // sx={imageSx}
239
- // src={processedImageSrc}
240
- // width="100%"
241
- // onClick={handleImageClick}
242
- // />
243
- // </Box>
244
- // );
245
- // };
246
- // export default ImageItem;
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useMemo } from "react";
3
+ import { Box, CardMedia, useTheme } from "@mui/material";
4
+ import { TickCircle } from "iconsax-react";
5
+ import CustomTickIcon from "../svg/Tick";
6
+ import { neutral } from "../../../color";
7
+ const initialAdjustments = {
8
+ temperature: 0, tint: 0, saturation: 0, vibrance: 0, exposure: 0, contrast: 0,
9
+ highlights: 0, shadows: 0, whites: 0, blacks: 0, clarity: 0, sharpness: 0,
10
+ };
11
+ const imgStyle = {
12
+ transition: "transform .135s cubic-bezier(0.0,0.0,0.2,1),opacity linear .15s",
13
+ width: "100%",
14
+ // objectFit: "contain",
15
+ //height: "100%",
16
+ };
17
+ const selectedImgStyle = {
18
+ borderRadius: "8px",
19
+ transform: "translateZ(0px) scale3d(1, 1, 1)",
20
+ // transition: "transform .135s cubic-bezier(0.0,0.0,0.2,1),opacity linear .15s"
21
+ };
22
+ const GalleryImageItem = (props) => {
23
+ const { photo, margin, adjustments, isSelected = false } = props;
24
+ const theme = useTheme();
25
+ const hasAdjustments = useMemo(() => {
26
+ if (!adjustments)
27
+ return false;
28
+ return Object.keys(initialAdjustments).some(key => adjustments[key] !== initialAdjustments[key]);
29
+ }, [adjustments]);
30
+ const styleHiddenGallery = useMemo(() => {
31
+ if (props.isHiddenGallery) {
32
+ return { filter: "blur(20px)", pointerEvents: "none" };
33
+ }
34
+ else {
35
+ return {};
36
+ }
37
+ }, [props.isHiddenGallery]);
38
+ const commonStyle = useMemo(() => {
39
+ return {
40
+ backgroundColor: neutral.white,
41
+ overflow: "hidden",
42
+ position: "relative",
43
+ width: "100%",
44
+ aspectRatio: `${photo.width}/${photo.height}`,
45
+ };
46
+ }, [props.direction, props.photo]);
47
+ const handleImageClick = () => {
48
+ console.debug({
49
+ isFullMode: props.isFullScreenMode,
50
+ isShowGallery: props.isHiddenGallery,
51
+ }, "handleImageClick");
52
+ if (!props.isFullScreenMode && !props.isHiddenGallery) {
53
+ if (props.isSelectedMode) {
54
+ console.debug("handleImageClick with toggle select");
55
+ props.onToggleSelect();
56
+ }
57
+ else {
58
+ console.debug("handleImageClick with preview");
59
+ props.onPreview();
60
+ }
61
+ }
62
+ };
63
+ const handleImageSelectedIconClick = () => {
64
+ console.debug("handleImageSelectedIconClick");
65
+ if (!props.isFullScreenMode) {
66
+ if (!props.isSelectedMode) {
67
+ props.onSelectedMode();
68
+ }
69
+ props.onToggleSelect();
70
+ }
71
+ };
72
+ const imageSx = useMemo(() => {
73
+ const baseStyles = isSelected ? {
74
+ ...imgStyle,
75
+ ...selectedImgStyle,
76
+ ...styleHiddenGallery,
77
+ aspectRatio: `${photo.width}/${photo.height}`,
78
+ } : {
79
+ ...imgStyle,
80
+ ...styleHiddenGallery,
81
+ };
82
+ return {
83
+ ...baseStyles,
84
+ opacity: 1, // Previously depended on isProcessingComplete
85
+ transition: 'opacity 0.3s ease-in-out',
86
+ };
87
+ }, [isSelected, styleHiddenGallery, photo.width, photo.height]);
88
+ const boxNotSelected = useMemo(() => ({
89
+ margin,
90
+ // height: photo.height,
91
+ ...commonStyle,
92
+ transition: ".3s",
93
+ // "&:hover": { padding: "12px", backgroundColor: "primary.light1" },
94
+ "&:-webkit-transition": { transition: ".3s" },
95
+ "&:hover > .checkbox": { display: props.isHiddenGallery ? "" : "block" },
96
+ cursor: props.isFullScreenMode || props.isHiddenGallery ? "inherit" : "pointer",
97
+ backgroundColor: props.isFullScreenMode
98
+ ? "rgba(0,0,0,0.1)"
99
+ : "transparent",
100
+ }), [margin, commonStyle, props.isHiddenGallery, props.isFullScreenMode]);
101
+ const boxSelected = useMemo(() => ({
102
+ margin,
103
+ // height: photo.height,
104
+ ...commonStyle,
105
+ cursor: props.isFullScreenMode || props.isHiddenGallery ? "inherit" : "pointer",
106
+ transition: ".3s",
107
+ "&:-webkit-transition": { transition: ".3s" },
108
+ padding: { xs: "13px 12px", sm: "21.31px 25.56px 21.32px 27.68px" },
109
+ backgroundColor: theme.palette.light["Surface-Variant-2"],
110
+ }), [
111
+ margin,
112
+ commonStyle,
113
+ theme.palette.light,
114
+ props.isFullScreenMode,
115
+ props.isHiddenGallery,
116
+ ]);
117
+ const boxOuterSx = useMemo(() => {
118
+ if (props.isFullScreenMode) {
119
+ return {
120
+ margin,
121
+ ...commonStyle,
122
+ };
123
+ }
124
+ else {
125
+ if (props.isSelected) {
126
+ return { ...boxSelected };
127
+ }
128
+ else {
129
+ return { ...boxNotSelected };
130
+ }
131
+ }
132
+ }, [
133
+ props.isFullScreenMode,
134
+ photo.height,
135
+ photo.width,
136
+ commonStyle,
137
+ boxSelected,
138
+ boxNotSelected,
139
+ margin,
140
+ props.isSelected,
141
+ ]);
142
+ return (_jsxs(Box, { id: "Box_image", sx: boxOuterSx, className: "image", children: [!props.isHiddenGallery &&
143
+ (hasAdjustments && isSelected ? (_jsx(Box, { color: "primary.dark1", onClick: handleImageSelectedIconClick, sx: {
144
+ position: "absolute",
145
+ width: "19px",
146
+ height: "19px",
147
+ zIndex: "2",
148
+ left: "5px",
149
+ top: "5px",
150
+ borderRadius: { xs: "50%", sm: 0 },
151
+ }, className: "checkbox", children: _jsx(CustomTickIcon, {}) })) : (_jsx(Box, { color: "neutral.light2", onClick: handleImageSelectedIconClick, sx: {
152
+ position: "absolute",
153
+ width: "19px",
154
+ height: "19px",
155
+ zIndex: "1",
156
+ left: "5px",
157
+ top: "5px",
158
+ display: "none",
159
+ visibility: {
160
+ xs: props.isSelectedMode ? "visible" : "hidden",
161
+ sm: "visible",
162
+ },
163
+ }, className: "checkbox", children: _jsx(TickCircle, { style: { width: "24px", height: "24px" }, color: "white" }) }))), _jsx(CardMedia, { id: "card_media", component: "img", className: "image", loading: "lazy", alt: photo.alt ?? "Image", sx: imageSx, src: photo.src, width: "100%", onClick: handleImageClick })] }, photo.key));
164
+ };
165
+ export default GalleryImageItem;
@@ -0,0 +1,2 @@
1
+ declare const Tick: () => import("react/jsx-runtime").JSX.Element;
2
+ export default Tick;
@@ -0,0 +1,6 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { SvgIcon } from "@mui/material";
3
+ const Tick = () => {
4
+ return (_jsx(SvgIcon, { style: { width: "29px", height: "29px" }, children: _jsxs("svg", { width: "36", height: "36", viewBox: "0 0 36 36", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [_jsx("rect", { x: "5", y: "5", width: "22", height: "22", rx: "11", fill: "black" }), _jsx("path", { d: "M20 13L14.5 18.5L12 16", stroke: "white", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" })] }) }));
5
+ };
6
+ export default Tick;
@@ -1,6 +1,15 @@
1
1
  import { SelectChangeEvent } from "@mui/material";
2
2
  import { AdjustmentState, ImageItem, Controller, Preset } from './useHonchoEditor';
3
3
  import { Gallery, ResponseGalleryPaging } from '../../hooks/editor/type';
4
+ export interface PhotoData {
5
+ key: string;
6
+ src: string;
7
+ width: number;
8
+ height: number;
9
+ alt: string;
10
+ isSelected: boolean;
11
+ originalData: Gallery;
12
+ }
4
13
  export interface ControllerBulk {
5
14
  onGetImage(firebaseUid: string, imageID: string): Promise<Gallery>;
6
15
  getImageList(firebaseUid: string, eventID: string, page: number): Promise<ResponseGalleryPaging>;
@@ -11,17 +20,26 @@ export interface ControllerBulk {
11
20
  deletePreset(firebaseUid: string, presetId: string): Promise<void>;
12
21
  }
13
22
  export declare function useHonchoEditorBulk(controllerBulk: Controller, eventID: string, firebaseUid: string): {
23
+ imageCollection: PhotoData[];
24
+ isSelectedMode: boolean;
25
+ isLoading: boolean;
26
+ error: string | null;
27
+ selectedImageIds: string[];
28
+ handleSelectedMode: () => void;
29
+ handleToggleSelect: (photoToToggle: PhotoData) => () => void;
30
+ handlePreview: (photo: PhotoData) => () => void;
31
+ handleBackCallbackBulk: () => void;
14
32
  isBulkEditing: boolean;
15
33
  selectedImages: string;
16
34
  imageList: ImageItem[];
17
- selectedImageIds: Set<string>;
35
+ currentBatch: import("../useAdjustmentHistoryBatch").BatchAdjustmentState;
36
+ selectedIds: string[];
37
+ allImageIds: string[];
18
38
  adjustmentsMap: Map<string, AdjustmentState>;
19
39
  selectedBulkPreset: string;
20
- handleFileChangeBulk: (event: React.ChangeEvent<HTMLInputElement>) => void;
21
40
  handleToggleImageSelection: (imageId: string) => void;
22
41
  toggleBulkEditing: () => void;
23
42
  handleSelectBulkPreset: (event: SelectChangeEvent<string>) => void;
24
- handleBackCallbackBulk: () => void;
25
43
  setTempScore: (value: number) => void;
26
44
  setTintScore: (value: number) => void;
27
45
  setVibranceScore: (value: number) => void;
@@ -1,81 +1,83 @@
1
1
  'use client';
2
- import { useState, useCallback, useEffect } from 'react';
2
+ import { useState, useCallback, useEffect, useMemo } from 'react';
3
3
  import { useAdjustmentHistory } from '../useAdjustmentHistory';
4
4
  import { useAdjustmentHistoryBatch } from '../useAdjustmentHistoryBatch';
5
+ // Helper function to map the API response to the format our UI component needs
6
+ const mapGalleryToPhotoData = (gallery) => ({
7
+ key: gallery.id,
8
+ src: gallery.raw_edited?.path || gallery.download?.path || '',
9
+ width: gallery.raw_edited?.width || 1, // Default to 1 to prevent division by zero
10
+ height: gallery.raw_edited?.height || 1,
11
+ alt: gallery.id || 'gallery image',
12
+ isSelected: false, // All images start as unselected
13
+ originalData: gallery,
14
+ });
5
15
  const initialAdjustments = {
6
16
  tempScore: 0, tintScore: 0, vibranceScore: 0, exposureScore: 0, highlightsScore: 0, shadowsScore: 0,
7
17
  whitesScore: 0, blacksScore: 0, saturationScore: 0, contrastScore: 0, clarityScore: 0, sharpnessScore: 0,
8
18
  };
9
19
  const clamp = (value) => Math.max(-100, Math.min(100, value));
20
+ function mapColorAdjustmentToAdjustmentState(adj) {
21
+ return {
22
+ tempScore: adj.temperature || 0,
23
+ tintScore: adj.tint || 0,
24
+ vibranceScore: adj.vibrance || 0,
25
+ saturationScore: adj.saturation || 0,
26
+ exposureScore: adj.exposure || 0,
27
+ highlightsScore: adj.highlights || 0,
28
+ shadowsScore: adj.shadows || 0,
29
+ whitesScore: adj.whites || 0,
30
+ blacksScore: adj.blacks || 0,
31
+ contrastScore: adj.contrast || 0,
32
+ clarityScore: adj.clarity || 0,
33
+ sharpnessScore: adj.sharpness || 0,
34
+ };
35
+ }
10
36
  export function useHonchoEditorBulk(controllerBulk, eventID, firebaseUid) {
11
- const { currentState, actions: historyActions, historyInfo } = useAdjustmentHistory(initialAdjustments);
12
- const { currentBatch, selectedIds, allImageIds, actions: batchActions, } = useAdjustmentHistoryBatch({
13
- maxSize: historyInfo.historySize,
14
- defaultAdjustmentState: currentState,
15
- });
37
+ const { currentState, actions: historyActions, } = useAdjustmentHistory(initialAdjustments);
38
+ const { currentBatch, selectedIds, allImageIds, actions: batchActions, historyInfo } = useAdjustmentHistoryBatch({});
16
39
  // State for Bulk Editing
40
+ const [imageCollection, setImageCollection] = useState([]);
41
+ const [isSelectedMode, setIsSelectedMode] = useState(false);
42
+ const [isLoading, setIsLoading] = useState(true);
43
+ const [error, setError] = useState(null);
17
44
  const [isBulkEditing, setIsBulkEditing] = useState(false);
18
45
  const [selectedImages, setSelectedImages] = useState('Select');
19
46
  const [imageList, setImageList] = useState([]);
20
- const [selectedImageIds, setSelectedImageIds] = useState(new Set());
21
47
  const [adjustmentsMap, setAdjustmentsMap] = useState(new Map());
22
48
  const [selectedBulkPreset, setSelectedBulkPreset] = useState('preset1');
49
+ const [isEditorReady, setIsEditorReady] = useState(false);
50
+ const selectedImageIds = useMemo(() => imageCollection.filter(p => p.isSelected).map(p => p.key), [imageCollection]);
23
51
  const handleBackCallbackBulk = useCallback(() => {
24
- // 1. Convert the Set of selected IDs into an array to get the last item.
25
- const selectedIdsArray = Array.from(selectedImageIds);
26
- // 2. Determine which ID to send back.
27
- // - If any images are selected, get the ID of the LAST one in the array.
28
- // - If no images are selected, fall back to using the eventID.
29
- const idToSendBack = selectedIdsArray.length > 0
30
- ? selectedIdsArray[selectedIdsArray.length - 1]
31
- : eventID;
32
- // 3. Check if we have an ID to send, otherwise log a warning.
33
- if (!idToSendBack) {
34
- console.warn("handleBack called, but no selected image ID or eventID was available.");
35
- window.history.back();
36
- return;
37
- }
38
- // 4. Call the controller with the chosen ID (either the last imageId or the eventId).
39
- controllerBulk.handleBack(firebaseUid, idToSendBack);
52
+ const lastSelectedId = selectedImageIds.length > 0 ? selectedImageIds[selectedImageIds.length - 1] : eventID;
53
+ controllerBulk.handleBack(firebaseUid, lastSelectedId);
40
54
  }, [controllerBulk, firebaseUid, selectedImageIds, eventID]);
41
- const handleFileChangeBulk = (event) => {
42
- const files = event.target?.files;
43
- if (!files || files.length <= 1) {
44
- // If it's not a bulk operation, we clear the state.
45
- setIsBulkEditing(false);
46
- setImageList([]);
47
- setSelectedImageIds(new Set());
48
- setAdjustmentsMap(new Map());
49
- return;
55
+ const handleSelectedMode = useCallback(() => {
56
+ setIsSelectedMode(true);
57
+ }, []);
58
+ const handleToggleSelect = useCallback((photoToToggle) => () => {
59
+ setImageCollection(currentCollection => currentCollection.map(photo => photo.key === photoToToggle.key
60
+ ? { ...photo, isSelected: !photo.isSelected }
61
+ : photo));
62
+ // Automatically enter selection mode on first selection
63
+ if (!isSelectedMode) {
64
+ setIsSelectedMode(true);
50
65
  }
51
- ;
52
- setIsBulkEditing(true);
53
- const newImageList = Array.from(files).map((file, index) => ({
54
- id: `${file.name}-${Date.now()}-${index}`,
55
- name: file.name,
56
- file: file,
57
- url: URL.createObjectURL(file),
58
- }));
59
- const newAdjustmentsMap = new Map();
60
- newImageList.forEach(image => {
61
- newAdjustmentsMap.set(image.id, { ...initialAdjustments });
62
- });
63
- setAdjustmentsMap(newAdjustmentsMap);
64
- setImageList(newImageList);
65
- setSelectedImageIds(new Set(newImageList.map(img => img.id)));
66
- };
67
- const handleToggleImageSelection = useCallback((imageId) => {
68
- const newSelectedIds = new Set(selectedImageIds);
69
- if (newSelectedIds.has(imageId)) {
70
- if (newSelectedIds.size > 1) { // Prevent deselecting the last image
71
- newSelectedIds.delete(imageId);
72
- }
73
- }
74
- else {
75
- newSelectedIds.add(imageId);
76
- }
77
- setSelectedImageIds(newSelectedIds);
78
- }, [selectedImageIds]);
66
+ }, [isSelectedMode]);
67
+ const handlePreview = useCallback((photo) => () => {
68
+ console.log("Previewing image:", photo.key);
69
+ }, []);
70
+ // const handleToggleImageSelection = useCallback((imageId: string) => {
71
+ // const newSelectedIds = new Set(selectedImageIds);
72
+ // if (newSelectedIds.has(imageId)) {
73
+ // if (newSelectedIds.size > 1) { // Prevent deselecting the last image
74
+ // newSelectedIds.delete(imageId);
75
+ // }
76
+ // } else {
77
+ // newSelectedIds.add(imageId);
78
+ // }
79
+ // setSelectedImageIds(newSelectedIds);
80
+ // }, [selectedImageIds]);
79
81
  const toggleBulkEditing = () => {
80
82
  setIsBulkEditing(prev => {
81
83
  const isNowBulk = !prev;
@@ -85,19 +87,6 @@ export function useHonchoEditorBulk(controllerBulk, eventID, firebaseUid) {
85
87
  };
86
88
  const handleSelectBulkPreset = (event) => setSelectedBulkPreset(event.target.value);
87
89
  // This factory creates functions that adjust a value for all selected images
88
- const createAbsoluteSetter = (key, setter) => (value) => {
89
- setter(value); // Update UI slider
90
- if (isBulkEditing) {
91
- setAdjustmentsMap(prevMap => {
92
- const newMap = new Map(prevMap);
93
- selectedImageIds.forEach(id => {
94
- const currentState = newMap.get(id) || initialAdjustments;
95
- newMap.set(id, { ...currentState, [key]: value });
96
- });
97
- return newMap;
98
- });
99
- }
100
- };
101
90
  const updateAdjustments = useCallback((newValues) => {
102
91
  const newState = { ...currentState, ...newValues };
103
92
  historyActions.pushState(newState);
@@ -108,18 +97,6 @@ export function useHonchoEditorBulk(controllerBulk, eventID, firebaseUid) {
108
97
  const newValue = clamp(currentValue + amount);
109
98
  updateAdjustments({ [key]: newValue });
110
99
  };
111
- useEffect(() => {
112
- if (!isBulkEditing)
113
- return;
114
- setAdjustmentsMap(prevMap => {
115
- const newMap = new Map(prevMap);
116
- selectedImageIds.forEach(id => {
117
- // Apply the new global state to each selected image
118
- newMap.set(id, currentState);
119
- });
120
- return newMap;
121
- });
122
- }, [currentState, selectedImageIds, isBulkEditing]);
123
100
  const setTempScore = (value) => updateAdjustments({ tempScore: value });
124
101
  const setTintScore = (value) => updateAdjustments({ tintScore: value });
125
102
  const setVibranceScore = (value) => updateAdjustments({ vibranceScore: value });
@@ -180,18 +157,57 @@ export function useHonchoEditorBulk(controllerBulk, eventID, firebaseUid) {
180
157
  const handleBulkSharpnessDecrease = createRelativeAdjuster('sharpnessScore', -5);
181
158
  const handleBulkSharpnessIncrease = createRelativeAdjuster('sharpnessScore', 5);
182
159
  const handleBulkSharpnessIncreaseMax = createRelativeAdjuster('sharpnessScore', 20);
160
+ // Extract selected image IDs for other operations (like applying bulk adjustments)
161
+ useEffect(() => {
162
+ if (eventID && firebaseUid) {
163
+ setIsLoading(true);
164
+ setError(null);
165
+ controllerBulk.getImageList(firebaseUid, eventID, 1)
166
+ .then(response => {
167
+ const images = response.gallery;
168
+ // Prepare the initial data for the batch history hook
169
+ const imageConfigs = images.map(img => ({
170
+ imageId: img.id,
171
+ adjustment: img.editor_config?.color_adjustment
172
+ ? mapColorAdjustmentToAdjustmentState(img.editor_config.color_adjustment)
173
+ : initialAdjustments
174
+ }));
175
+ // Populate the batch history with all fetched images
176
+ batchActions.setSelection(imageConfigs);
177
+ // Immediately clear the selection so no images are selected by default
178
+ batchActions.clearSelection();
179
+ })
180
+ .catch(err => {
181
+ console.error("Failed to fetch gallery:", err);
182
+ setError(err.message || "Could not load images.");
183
+ })
184
+ .finally(() => {
185
+ setIsLoading(false);
186
+ });
187
+ }
188
+ }, [eventID, firebaseUid, controllerBulk, batchActions]);
183
189
  return {
190
+ imageCollection,
191
+ isSelectedMode,
192
+ isLoading,
193
+ error,
194
+ selectedImageIds,
195
+ // Gallery Handlers
196
+ handleSelectedMode,
197
+ handleToggleSelect,
198
+ handlePreview,
199
+ handleBackCallbackBulk,
184
200
  isBulkEditing,
185
201
  selectedImages,
186
202
  imageList,
187
- selectedImageIds,
203
+ currentBatch,
204
+ selectedIds,
205
+ allImageIds,
188
206
  adjustmentsMap,
189
207
  selectedBulkPreset,
190
- handleFileChangeBulk,
191
- handleToggleImageSelection,
208
+ handleToggleImageSelection: batchActions.toggleSelection,
192
209
  toggleBulkEditing,
193
210
  handleSelectBulkPreset,
194
- handleBackCallbackBulk,
195
211
  // Bulk Adjustment Handlers
196
212
  setTempScore,
197
213
  setTintScore,
package/dist/index.d.ts CHANGED
@@ -20,6 +20,8 @@ export { default as HModalMobile } from './components/editor/HModalMobile';
20
20
  export { default as HPresetOptionsMenu } from './components/editor/HPresetOptionMenu';
21
21
  export { HAlertInternetBox, HAlertCopyBox, HAlertInternetConnectionBox, HAlertPresetSave } from './components/editor/HAlertBox';
22
22
  export { useHonchoEditorBulk } from './hooks/editor/useHonchoEditorBulk';
23
+ export { default as AlbumImageGallery } from './components/editor/GalleryAlbum/AlbumImageGallery';
24
+ export { default as GalleryImageItem } from './components/editor/GalleryAlbum/ImageItem';
23
25
  export { useAdjustmentHistory, type UseAdjustmentHistoryReturn, type HistoryInfo, type HistoryActions, type HistoryConfig } from './hooks/useAdjustmentHistory';
24
26
  export { useAdjustmentHistoryBatch, type UseAdjustmentHistoryBatchReturn, type BatchAdjustmentState, type ImageAdjustmentConfig, type BatchHistoryInfo, type BatchHistoryActions, type BatchHistoryConfig } from './hooks/useAdjustmentHistoryBatch';
25
27
  export { default as useColors } from './themes/colors';
package/dist/index.js CHANGED
@@ -18,6 +18,8 @@ export { default as HModalMobile } from './components/editor/HModalMobile';
18
18
  export { default as HPresetOptionsMenu } from './components/editor/HPresetOptionMenu';
19
19
  export { HAlertInternetBox, HAlertCopyBox, HAlertInternetConnectionBox, HAlertPresetSave } from './components/editor/HAlertBox';
20
20
  export { useHonchoEditorBulk } from './hooks/editor/useHonchoEditorBulk';
21
+ export { default as AlbumImageGallery } from './components/editor/GalleryAlbum/AlbumImageGallery';
22
+ export { default as GalleryImageItem } from './components/editor/GalleryAlbum/ImageItem';
21
23
  // --- History Hooks ---
22
24
  export { useAdjustmentHistory } from './hooks/useAdjustmentHistory';
23
25
  export { useAdjustmentHistoryBatch } from './hooks/useAdjustmentHistoryBatch';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yogiswara/honcho-editor-ui",
3
- "version": "2.0.7",
3
+ "version": "2.1.1",
4
4
  "description": "A complete UI component library for the Honcho photo editor.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -49,6 +49,7 @@
49
49
  "keywords": [],
50
50
  "author": "",
51
51
  "dependencies": {
52
+ "iconsax-react": "^0.0.8",
52
53
  "react-responsive-masonry": "^2.7.1"
53
54
  }
54
55
  }