@yogiswara/honcho-editor-ui 2.7.13 → 2.7.14

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 (139) hide show
  1. package/package.json +1 -1
  2. package/dist/color.d.ts +0 -9
  3. package/dist/color.js +0 -9
  4. package/dist/components/editor/GalleryAlbum/AlbumImageGallery.d.ts +0 -8
  5. package/dist/components/editor/GalleryAlbum/AlbumImageGallery.js +0 -28
  6. package/dist/components/editor/GalleryAlbum/ImageItem.d.ts +0 -10
  7. package/dist/components/editor/GalleryAlbum/ImageItem.js +0 -81
  8. package/dist/components/editor/HAccordionAspectRatio.d.ts +0 -14
  9. package/dist/components/editor/HAccordionAspectRatio.js +0 -102
  10. package/dist/components/editor/HAccordionColor.d.ts +0 -16
  11. package/dist/components/editor/HAccordionColor.js +0 -282
  12. package/dist/components/editor/HAccordionColorAdjustment.d.ts +0 -35
  13. package/dist/components/editor/HAccordionColorAdjustment.js +0 -31
  14. package/dist/components/editor/HAccordionDetails.d.ts +0 -12
  15. package/dist/components/editor/HAccordionDetails.js +0 -157
  16. package/dist/components/editor/HAccordionLight.d.ts +0 -20
  17. package/dist/components/editor/HAccordionLight.js +0 -414
  18. package/dist/components/editor/HAccordionPreset.d.ts +0 -23
  19. package/dist/components/editor/HAccordionPreset.js +0 -50
  20. package/dist/components/editor/HAlertBox.d.ts +0 -8
  21. package/dist/components/editor/HAlertBox.js +0 -55
  22. package/dist/components/editor/HAspectRatioMobile.d.ts +0 -0
  23. package/dist/components/editor/HAspectRatioMobile.js +0 -1
  24. package/dist/components/editor/HBulkAccordionColorAdjustment.d.ts +0 -55
  25. package/dist/components/editor/HBulkAccordionColorAdjustment.js +0 -31
  26. package/dist/components/editor/HBulkAccordionColorAdjustmentColors.d.ts +0 -20
  27. package/dist/components/editor/HBulkAccordionColorAdjustmentColors.js +0 -121
  28. package/dist/components/editor/HBulkAccordionColorAdjustmentDetails.d.ts +0 -12
  29. package/dist/components/editor/HBulkAccordionColorAdjustmentDetails.js +0 -65
  30. package/dist/components/editor/HBulkAccordionColorAdjustmentLight.d.ts +0 -28
  31. package/dist/components/editor/HBulkAccordionColorAdjustmentLight.js +0 -177
  32. package/dist/components/editor/HBulkColorAdjustmentMobile.d.ts +0 -53
  33. package/dist/components/editor/HBulkColorAdjustmentMobile.js +0 -16
  34. package/dist/components/editor/HBulkColorMobile.d.ts +0 -20
  35. package/dist/components/editor/HBulkColorMobile.js +0 -121
  36. package/dist/components/editor/HBulkDetailsMobile.d.ts +0 -12
  37. package/dist/components/editor/HBulkDetailsMobile.js +0 -65
  38. package/dist/components/editor/HBulkLightMobile.d.ts +0 -28
  39. package/dist/components/editor/HBulkLightMobile.js +0 -192
  40. package/dist/components/editor/HBulkPreset.d.ts +0 -24
  41. package/dist/components/editor/HBulkPreset.js +0 -43
  42. package/dist/components/editor/HBulkPresetMobile.d.ts +0 -15
  43. package/dist/components/editor/HBulkPresetMobile.js +0 -26
  44. package/dist/components/editor/HDialogBox.d.ts +0 -18
  45. package/dist/components/editor/HDialogBox.js +0 -51
  46. package/dist/components/editor/HDialogCopy.d.ts +0 -40
  47. package/dist/components/editor/HDialogCopy.js +0 -80
  48. package/dist/components/editor/HFooter.d.ts +0 -12
  49. package/dist/components/editor/HFooter.js +0 -24
  50. package/dist/components/editor/HHeaderEditor.d.ts +0 -17
  51. package/dist/components/editor/HHeaderEditor.js +0 -36
  52. package/dist/components/editor/HImageEditorBulkDekstop.d.ts +0 -15
  53. package/dist/components/editor/HImageEditorBulkDekstop.js +0 -29
  54. package/dist/components/editor/HImageEditorBulkMobile.d.ts +0 -72
  55. package/dist/components/editor/HImageEditorBulkMobile.js +0 -81
  56. package/dist/components/editor/HImageEditorDekstop.d.ts +0 -15
  57. package/dist/components/editor/HImageEditorDekstop.js +0 -29
  58. package/dist/components/editor/HImageEditorMobile.d.ts +0 -51
  59. package/dist/components/editor/HImageEditorMobile.js +0 -92
  60. package/dist/components/editor/HImageEditorMobileLayout.d.ts +0 -14
  61. package/dist/components/editor/HImageEditorMobileLayout.js +0 -58
  62. package/dist/components/editor/HModalEditorDekstop.d.ts +0 -13
  63. package/dist/components/editor/HModalEditorDekstop.js +0 -22
  64. package/dist/components/editor/HModalMobile.d.ts +0 -13
  65. package/dist/components/editor/HModalMobile.js +0 -7
  66. package/dist/components/editor/HPresetDelete.d.ts +0 -7
  67. package/dist/components/editor/HPresetDelete.js +0 -7
  68. package/dist/components/editor/HPresetOptionMenu.d.ts +0 -9
  69. package/dist/components/editor/HPresetOptionMenu.js +0 -20
  70. package/dist/components/editor/HSliderColorMobile.d.ts +0 -16
  71. package/dist/components/editor/HSliderColorMobile.js +0 -270
  72. package/dist/components/editor/HSliderDetailsMobile.d.ts +0 -12
  73. package/dist/components/editor/HSliderDetailsMobile.js +0 -154
  74. package/dist/components/editor/HSliderLightMobile.d.ts +0 -20
  75. package/dist/components/editor/HSliderLightMobile.js +0 -420
  76. package/dist/components/editor/HTabAspectRatioMobile.d.ts +0 -0
  77. package/dist/components/editor/HTabAspectRatioMobile.js +0 -1
  78. package/dist/components/editor/HTabColorAdjustmentMobile.d.ts +0 -33
  79. package/dist/components/editor/HTabColorAdjustmentMobile.js +0 -16
  80. package/dist/components/editor/HTabPresetMobile.d.ts +0 -14
  81. package/dist/components/editor/HTabPresetMobile.js +0 -10
  82. package/dist/components/editor/HTextField.d.ts +0 -14
  83. package/dist/components/editor/HTextField.js +0 -51
  84. package/dist/components/editor/HWatermarkView.d.ts +0 -6
  85. package/dist/components/editor/HWatermarkView.js +0 -16
  86. package/dist/components/editor/svg/Tick.d.ts +0 -2
  87. package/dist/components/editor/svg/Tick.js +0 -6
  88. package/dist/components/modal/HModalDialog.d.ts +0 -12
  89. package/dist/components/modal/HModalDialog.js +0 -18
  90. package/dist/components/modal/HModalRename.d.ts +0 -14
  91. package/dist/components/modal/HModalRename.js +0 -35
  92. package/dist/hooks/demo/HonchoEditorBulkDemo.d.ts +0 -3
  93. package/dist/hooks/demo/HonchoEditorBulkDemo.js +0 -410
  94. package/dist/hooks/demo/HonchoEditorSingleCleanDemo.d.ts +0 -3
  95. package/dist/hooks/demo/HonchoEditorSingleCleanDemo.js +0 -354
  96. package/dist/hooks/demo/index.d.ts +0 -2
  97. package/dist/hooks/demo/index.js +0 -2
  98. package/dist/hooks/editor/type.d.ts +0 -174
  99. package/dist/hooks/editor/type.js +0 -1
  100. package/dist/hooks/editor/useHonchoEditorBulk.d.ts +0 -96
  101. package/dist/hooks/editor/useHonchoEditorBulk.js +0 -427
  102. package/dist/hooks/editor/useHonchoEditorSingle.d.ts +0 -44
  103. package/dist/hooks/editor/useHonchoEditorSingle.js +0 -162
  104. package/dist/hooks/useAdjustmentHistory.d.ts +0 -97
  105. package/dist/hooks/useAdjustmentHistory.js +0 -493
  106. package/dist/hooks/useAdjustmentHistoryBatch.d.ts +0 -177
  107. package/dist/hooks/useAdjustmentHistoryBatch.js +0 -1189
  108. package/dist/hooks/useGallerySwipe.d.ts +0 -36
  109. package/dist/hooks/useGallerySwipe.js +0 -344
  110. package/dist/hooks/usePaging.d.ts +0 -89
  111. package/dist/hooks/usePaging.js +0 -211
  112. package/dist/hooks/usePreset.d.ts +0 -82
  113. package/dist/hooks/usePreset.js +0 -344
  114. package/dist/index.d.ts +0 -41
  115. package/dist/index.js +0 -44
  116. package/dist/lib/context/EditorContext.d.ts +0 -28
  117. package/dist/lib/context/EditorContext.js +0 -60
  118. package/dist/lib/context/EditorProcessingService.d.ts +0 -36
  119. package/dist/lib/context/EditorProcessingService.js +0 -249
  120. package/dist/lib/editor/honcho-editor.d.ts +0 -324
  121. package/dist/lib/editor/honcho-editor.js +0 -825
  122. package/dist/lib/hooks/useEditor.d.ts +0 -22
  123. package/dist/lib/hooks/useEditor.js +0 -35
  124. package/dist/lib/hooks/useEditorHeadless.d.ts +0 -34
  125. package/dist/lib/hooks/useEditorHeadless.js +0 -207
  126. package/dist/lib/hooks/useImageProcessor.d.ts +0 -18
  127. package/dist/lib/hooks/useImageProcessor.js +0 -113
  128. package/dist/setupTests.d.ts +0 -1
  129. package/dist/setupTests.js +0 -1
  130. package/dist/themes/colors.d.ts +0 -12
  131. package/dist/themes/colors.js +0 -12
  132. package/dist/themes/honchoTheme.d.ts +0 -25
  133. package/dist/themes/honchoTheme.js +0 -94
  134. package/dist/utils/adjustment.d.ts +0 -6
  135. package/dist/utils/adjustment.js +0 -48
  136. package/dist/utils/imageLoader.d.ts +0 -11
  137. package/dist/utils/imageLoader.js +0 -48
  138. package/dist/utils/isMobile.d.ts +0 -1
  139. package/dist/utils/isMobile.js +0 -5
@@ -1,14 +0,0 @@
1
- import React from "react";
2
- type Preset = {
3
- id: string;
4
- name: string;
5
- };
6
- interface Props {
7
- presets: Preset[];
8
- selectedPreset: string | null;
9
- presetOptionModal: (event: React.MouseEvent<HTMLElement>, presetId: string) => void;
10
- onOpenPresetModal: () => void;
11
- onSelectPreset: (id: string) => void;
12
- }
13
- export default function HTabPresetMobile(props: Props): import("react/jsx-runtime").JSX.Element;
14
- export {};
@@ -1,10 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { Stack, IconButton, CardMedia, Button } from "@mui/material";
3
- import useHonchoTypography from "../../themes/honchoTheme";
4
- import useColors from '../../themes/colors';
5
- // Static `presets` array has been removed.
6
- export default function HTabPresetMobile(props) {
7
- const typography = useHonchoTypography();
8
- const colors = useColors();
9
- return (_jsx(_Fragment, { children: _jsxs(Stack, { direction: "column", spacing: 0, sx: { px: "0px", mx: "0px" }, children: [props.presets.map((preset) => (_jsxs(Stack, { direction: "row", alignItems: "center", justifyContent: "space-between", children: [_jsx(Button, { sx: { ...typography.bodyMedium, color: colors.surface, justifyContent: 'flex-start', flexGrow: 1, textTransform: 'none' }, onClick: () => props.onSelectPreset(preset.id), children: preset.name }), _jsxs(Stack, { direction: "row", alignItems: "center", spacing: 1, children: [props.selectedPreset === preset.id && (_jsx(CardMedia, { component: "img", image: "v1/svg/check-ratio-editor.svg", sx: { width: "20px", height: "20px", px: "2px" } })), _jsx(IconButton, { "aria-label": preset.name, sx: { px: "8px" }, onClick: (event) => props.presetOptionModal(event, preset.id), children: _jsx(CardMedia, { component: "img", image: "/v1/svg/dots-editor.svg", alt: "Options" }) })] })] }, preset.id))), _jsx(Button, { variant: "text", sx: { color: colors.surface, border: "1px solid", borderColor: colors.surface, borderRadius: "40px", mt: "12px", textTransform: 'none' }, onClick: props.onOpenPresetModal, children: "Create Preset" })] }) }));
10
- }
@@ -1,14 +0,0 @@
1
- import React from "react";
2
- interface Props {
3
- valueName: string;
4
- setName: (event: React.ChangeEvent<HTMLInputElement>) => void;
5
- }
6
- export declare function HTextField(props: Props): import("react/jsx-runtime").JSX.Element;
7
- interface PropsRename {
8
- valueName: string;
9
- setName: (event: React.ChangeEvent<HTMLInputElement>) => void;
10
- onSaveRenamePreset: () => void;
11
- onCancel: () => void;
12
- }
13
- export declare function HTextFieldRename(props: PropsRename): import("react/jsx-runtime").JSX.Element;
14
- export {};
@@ -1,51 +0,0 @@
1
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { TextField, Stack, Button } from "@mui/material";
3
- import useHonchoTypography from "../../themes/honchoTheme";
4
- import useColors from '../../themes/colors';
5
- export function HTextField(props) {
6
- const colors = useColors();
7
- const typography = useHonchoTypography();
8
- return (_jsx(_Fragment, { children: _jsx(TextField, { autoFocus: true, margin: "dense", id: "name", label: "Preset Name", type: "text", fullWidth: true, variant: "standard", defaultValue: props.valueName, onChange: props.setName, sx: {
9
- backgroundColor: "#F6F6F6",
10
- p: "7px",
11
- borderRadius: "6px",
12
- '& .MuiInputLabel-root': {
13
- color: colors.onSurfaceVariant, // A slightly dimmer color for the placeholder
14
- pt: '10px',
15
- pl: '10px',
16
- },
17
- '& .MuiInput-input': {
18
- color: colors.onSurface,
19
- },
20
- '& .MuiInputLabel-root.Mui-focused': {
21
- color: colors.onSurfaceVariant, // Or a different color if you prefer
22
- },
23
- } }) }));
24
- }
25
- export function HTextFieldRename(props) {
26
- const colors = useColors();
27
- const typography = useHonchoTypography();
28
- return (_jsx(_Fragment, { children: _jsxs(Stack, { direction: "column", spacing: 2, children: [_jsx(TextField, { autoFocus: true, type: "text", fullWidth: true, variant: "standard", defaultValue: props.valueName, onChange: props.setName, sx: {
29
- backgroundColor: "#F6F6F6",
30
- p: "7px",
31
- borderRadius: "6px",
32
- '& .MuiInputLabel-root': {
33
- pt: '10px',
34
- pl: '10px',
35
- },
36
- } }), _jsxs(Stack, { direction: "row", justifyContent: "end", alignItems: "center", children: [_jsx(Button, { color: "primary", onClick: props.onCancel, sx: {
37
- backgroundColor: colors.surface,
38
- color: colors.onSurface,
39
- '&:hover': {
40
- backgroundColor: colors.onSurfaceVariant1,
41
- },
42
- ...typography.titleMedium,
43
- }, children: "Cancel" }), _jsx(Button, { color: "primary", onClick: props.onSaveRenamePreset, sx: {
44
- backgroundColor: colors.surface,
45
- color: colors.onSurface,
46
- '&:hover': {
47
- backgroundColor: colors.onSurfaceVariant1,
48
- },
49
- ...typography.titleMedium,
50
- }, children: "Save" })] })] }) }));
51
- }
@@ -1,6 +0,0 @@
1
- interface Props {
2
- onSaveWatermark: () => void;
3
- onCancelWatermark: () => void;
4
- }
5
- export default function HWatermarkView(props: Props): import("react/jsx-runtime").JSX.Element;
6
- export {};
@@ -1,16 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { TextField, Button, Stack } from "@mui/material";
3
- import useHonchoTypography from "../../themes/honchoTheme";
4
- import useColors from '../../themes/colors';
5
- export default function HWatermarkView(props) {
6
- const colors = useColors();
7
- const typography = useHonchoTypography();
8
- return (_jsx(_Fragment, { children: _jsxs(Stack, { direction: "column", alignItems: "center", spacing: 5, sx: { pt: "20px", color: colors.surface }, children: [_jsx(TextField, { id: "watermark-name", label: "watermark name", variant: "standard", sx: {
9
- '& .MuiInput-underline:before': {
10
- borderBottomColor: colors.surface
11
- },
12
- '& .MuiInputBase-input': {
13
- color: colors.surface
14
- }
15
- } }), _jsxs(Stack, { direction: "row", spacing: 0.5, children: [_jsx(Button, { variant: "text", sx: { color: colors.surface }, children: "Landscape" }), _jsx(Button, { variant: "text", sx: { color: colors.surface }, children: "Portrait" }), _jsx(Button, { variant: "text", sx: { color: colors.surface }, children: "Square" })] })] }) }));
16
- }
@@ -1,2 +0,0 @@
1
- declare const Tick: () => import("react/jsx-runtime").JSX.Element;
2
- export default Tick;
@@ -1,6 +0,0 @@
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,12 +0,0 @@
1
- interface ModalDialogProps {
2
- isOpen: boolean;
3
- isLoading?: boolean;
4
- title?: string;
5
- description?: string;
6
- onCancel: () => void;
7
- onSubmit: () => void;
8
- cancelLabel?: string;
9
- submitLabel?: string;
10
- }
11
- declare function ModalDialog(props: ModalDialogProps): import("react/jsx-runtime").JSX.Element;
12
- export default ModalDialog;
@@ -1,18 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Button, Dialog, DialogActions, DialogContent, DialogTitle, Grid, Typography, useTheme, } from "@mui/material";
3
- import useHonchoTypography from "../../themes/honchoTheme";
4
- import useColors from '../../themes/colors';
5
- const defaultPaperStyle = {
6
- borderRadius: "28px",
7
- width: { xs: "100%", sm: "456px" },
8
- boxShadow: "none",
9
- };
10
- function ModalDialog(props) {
11
- const { isOpen, isLoading = false, title, description, onCancel, onSubmit, cancelLabel = "Cancel", submitLabel = "Confirm", } = props;
12
- const theme = useTheme();
13
- const typography = useHonchoTypography();
14
- const colors = useColors();
15
- return (_jsxs(Dialog, { disableScrollLock: true, open: isOpen, onClose: onCancel, "aria-labelledby": "dialog-title", PaperProps: { sx: defaultPaperStyle }, children: ["``", title && (_jsx(DialogTitle, { children: _jsx(Typography, { variant: "labelLarge", color: colors.onSurface, children: title }) })), _jsx(DialogContent, { sx: { padding: { xs: "24px 24px 0 24px", sm: "24px 24px 0 24px" } }, children: description && (_jsx(Typography, { sx: { color: "#656369", fontSize: 14 }, variant: "bodyMedium", children: description })) }), _jsx(DialogActions, { sx: { padding: 3 }, children: _jsxs(Grid, { container: true, direction: "row", justifyContent: "end", alignItems: "center", gap: 1, children: [_jsx(Button, { variant: "text", onClick: onCancel, children: cancelLabel }), _jsx(Button, { variant: "text", color: "error", disabled: isLoading, onClick: () => onSubmit(), children: submitLabel })] }) })] }));
16
- }
17
- ;
18
- export default ModalDialog;
@@ -1,14 +0,0 @@
1
- interface ModalRenameProps {
2
- isOpen: boolean;
3
- title?: string;
4
- description?: string;
5
- action?: React.ReactNode;
6
- onCancel: () => void;
7
- onSubmit: () => void;
8
- cancelLabel?: string;
9
- submitLabel?: string;
10
- valueName: string;
11
- setName: (event: React.ChangeEvent<HTMLInputElement>) => void;
12
- }
13
- declare function ModalRename(props: ModalRenameProps): import("react/jsx-runtime").JSX.Element;
14
- export default ModalRename;
@@ -1,35 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Button, Dialog, DialogActions, DialogContent, DialogTitle, Grid, Typography, useTheme, TextField, } from "@mui/material";
3
- import useColors from '../../themes/colors';
4
- import useHonchoTypography from "../../themes/honchoTheme";
5
- const defaultPaperStyle = {
6
- borderRadius: "28px",
7
- width: { xs: "100%", sm: "456px" },
8
- boxShadow: "none",
9
- };
10
- function ModalRename(props) {
11
- const { isOpen, title, onCancel, onSubmit, cancelLabel = "Cancel", submitLabel = "Confirm", valueName, setName } = props;
12
- const colors = useColors();
13
- const theme = useTheme();
14
- const typography = useHonchoTypography();
15
- return (_jsxs(Dialog, { disableScrollLock: true, open: isOpen, onClose: onCancel, "aria-labelledby": "dialog-title", PaperProps: { sx: defaultPaperStyle }, children: [title && (_jsx(DialogTitle, { children: _jsx(Typography, { variant: "labelLarge", color: colors.onSurface, children: title }) })), _jsx(DialogContent, { sx: { padding: { xs: "24px 24px 0 24px", sm: "24px 24px 0 24px" } }, children: _jsx(TextField, { autoFocus: true, margin: "dense", id: "name", label: "Preset Name", type: "text", fullWidth: true, variant: "standard", value: valueName, onChange: setName, sx: {
16
- backgroundColor: "#F6F6F6",
17
- p: "7px",
18
- borderRadius: "6px",
19
- '& .MuiInputLabel-root': {
20
- color: colors.onSurfaceVariant,
21
- pt: '10px',
22
- pl: '10px',
23
- },
24
- '& .MuiInput-input': {
25
- color: colors.onSurface,
26
- },
27
- '& .MuiInputLabel-root.Mui-focused': {
28
- color: colors.onSurfaceVariant,
29
- },
30
- } }) }), _jsx(DialogActions, { sx: { padding: 3 }, children: _jsxs(Grid, { container: true, direction: "row", justifyContent: "end", alignItems: "center", gap: 1, children: [_jsx(Button, { variant: "text", onClick: onCancel, children: cancelLabel }), _jsx(Button, { variant: "text", color: "info",
31
- // The "disabled" attribute has been removed
32
- onClick: onSubmit, children: submitLabel })] }) })] }));
33
- }
34
- ;
35
- export default ModalRename;
@@ -1,3 +0,0 @@
1
- import React from 'react';
2
- export declare const HonchoEditorBulkDemo: React.FC;
3
- export default HonchoEditorBulkDemo;
@@ -1,410 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import React, { useState, useMemo, useRef, useEffect } from 'react';
3
- import { Box, Container, Typography, Button, Grid, Card, CardContent, Checkbox, Chip, Alert, CircularProgress, ButtonGroup, Paper, Divider, FormControl, InputLabel, Select, MenuItem, Stack } from '@mui/material';
4
- import { useHonchoEditorBulk } from '../editor/useHonchoEditorBulk';
5
- import { useImageProcessor } from "../../lib/hooks/useImageProcessor";
6
- // Mock data for demonstration
7
- const createMockGallery = (id, adjustments) => ({
8
- id,
9
- uid: 'demo-user',
10
- event_id: 'demo-event',
11
- download: {
12
- key: `${id}-download`,
13
- path: `https://s3.ap-southeast-1.amazonaws.com/dev.pronto.ubersnap/event/689c514d225024b1172ed297/media/95d811da-72a1-4e97-91cc-94446f0a10ad/original.jpeg`,
14
- size: 1024000,
15
- width: 800,
16
- height: 600,
17
- },
18
- download_edited: {
19
- key: `${id}-download-edited`,
20
- path: `https://s3.ap-southeast-1.amazonaws.com/dev.pronto.ubersnap/event/689c514d225024b1172ed297/media/95d811da-72a1-4e97-91cc-94446f0a10ad/original.jpeg`,
21
- size: 1024000,
22
- width: 800,
23
- height: 600,
24
- },
25
- thumbnail: {
26
- key: `${id}-thumb`,
27
- path: `https://s3.ap-southeast-1.amazonaws.com/dev.pronto.ubersnap/event/689c514d225024b1172ed297/media/95d811da-72a1-4e97-91cc-94446f0a10ad/original.jpeg`,
28
- size: 50000,
29
- width: 300,
30
- height: 200,
31
- },
32
- thumbnail_edited: {
33
- key: `${id}-thumb-edited`,
34
- path: `https://s3.ap-southeast-1.amazonaws.com/dev.pronto.ubersnap/event/689c514d225024b1172ed297/media/95d811da-72a1-4e97-91cc-94446f0a10ad/thumbnail.jpeg`,
35
- size: 50000,
36
- width: 300,
37
- height: 200,
38
- },
39
- is_original: true,
40
- available: true,
41
- show_gallery: true,
42
- editor_config: {
43
- color_adjustment: {
44
- temperature: adjustments?.tempScore || 0,
45
- tint: adjustments?.tintScore || 0,
46
- vibrance: adjustments?.vibranceScore || 0,
47
- saturation: adjustments?.saturationScore || 0,
48
- exposure: adjustments?.exposureScore || 0,
49
- highlights: adjustments?.highlightsScore || 0,
50
- shadows: adjustments?.shadowsScore || 0,
51
- whites: adjustments?.whitesScore || 0,
52
- blacks: adjustments?.blacksScore || 0,
53
- contrast: adjustments?.contrastScore || 0,
54
- clarity: adjustments?.clarityScore || 0,
55
- sharpness: adjustments?.sharpnessScore || 0,
56
- },
57
- transformation_adjustment: [],
58
- watermarks: [],
59
- },
60
- log: {
61
- created_at: new Date().toISOString(),
62
- updated_at: new Date().toISOString(),
63
- },
64
- });
65
- // Mock Controller implementation
66
- const createMockController = () => {
67
- console.log('[Controller] 🏭 createMockController() called - Creating new mock controller instance for bulk editor');
68
- // Generate mock images from 1 to 1277 using picsum.dev static URLs
69
- const mockImages = [];
70
- for (let i = 1; i <= 1000; i++) {
71
- const id = i.toString();
72
- // let adjustments: Partial<AdjustmentState> | undefined;
73
- mockImages.push(createMockGallery(id, undefined));
74
- }
75
- return {
76
- onGetImage: async (uid, imageId) => {
77
- console.log(`[Controller] 📷 onGetImage called: uid=${uid}, imageId=${imageId}`);
78
- await new Promise(resolve => setTimeout(resolve, 500));
79
- const image = mockImages.find(img => img.id === imageId);
80
- if (!image) {
81
- console.error(`[Controller] ❌ Image ${imageId} not found`);
82
- throw new Error(`Image ${imageId} not found`);
83
- }
84
- console.log(`[Controller] 📷 onGetImage returning image:`, image.id);
85
- return image;
86
- },
87
- getImageList: async (uid, eventId, page) => {
88
- console.log(`[Controller] 📋 getImageList called: uid=${uid}, eventId=${eventId}, page=${page}`);
89
- await new Promise(resolve => setTimeout(resolve, 800));
90
- const pageSize = 1000; // Increased page size for better UX with more images
91
- const startIndex = (page - 1) * pageSize;
92
- const endIndex = startIndex + pageSize;
93
- const pageImages = mockImages.slice(startIndex, endIndex);
94
- const result = {
95
- gallery: pageImages,
96
- limit: pageSize,
97
- current_page: page,
98
- prev_page: page > 1 ? page - 1 : 0,
99
- next_page: endIndex < mockImages.length ? page + 1 : 0,
100
- sum_of_image: pageImages.length,
101
- };
102
- console.log(`[Controller] 📋 getImageList returning ${pageImages.length} images for page ${page} (total available: ${mockImages.length})`, result);
103
- return result;
104
- },
105
- syncConfig: async (uid) => {
106
- console.log(`[Controller] 🔄 syncConfig called: uid=${uid}`);
107
- await new Promise(resolve => setTimeout(resolve, 200));
108
- console.log(`[Controller] 🔄 syncConfig completed for uid=${uid}`);
109
- },
110
- handleBack: (uid, imageId) => {
111
- console.log(`[Controller] ⬅️ handleBack called: uid=${uid}, imageId=${imageId}`);
112
- console.log(`[Controller] ⬅️ Back to: ${imageId}`);
113
- },
114
- getPresets: async (uid) => {
115
- console.log(`[Controller] 🎨 getPresets called: uid=${uid}`);
116
- await new Promise(resolve => setTimeout(resolve, 300));
117
- const presets = [
118
- { id: '1', name: 'Warm Sunset', is_default: false, temperature: 15, tint: 5, saturation: 8, vibrance: 12, exposure: 2, contrast: 5, highlights: -10, shadows: 8, whites: 3, blacks: -5, clarity: 4, sharpness: 6 },
119
- { id: '2', name: 'Cool Morning', is_default: false, temperature: -12, tint: -3, saturation: -2, vibrance: 5, exposure: 1, contrast: 3, highlights: -5, shadows: 12, whites: 8, blacks: -8, clarity: 6, sharpness: 4 },
120
- { id: '3', name: 'High Contrast', is_default: false, temperature: 0, tint: 0, saturation: 5, vibrance: 8, exposure: 0, contrast: 20, highlights: -15, shadows: 15, whites: 10, blacks: -10, clarity: 15, sharpness: 8 },
121
- ];
122
- console.log(`[Controller] 🎨 getPresets returning ${presets.length} presets`, presets.map(p => ({ id: p.id, name: p.name })));
123
- return presets;
124
- },
125
- createPreset: async (uid, name, settings) => {
126
- console.log(`[Controller] ➕ createPreset called: uid=${uid}, name=${name}`, settings);
127
- await new Promise(resolve => setTimeout(resolve, 500));
128
- console.log(`[Controller] ➕ createPreset completed: ${name}`);
129
- },
130
- deletePreset: async (uid, presetId) => {
131
- console.log(`[Controller] 🗑️ deletePreset called: uid=${uid}, presetId=${presetId}`);
132
- await new Promise(resolve => setTimeout(resolve, 300));
133
- console.log(`[Controller] 🗑️ deletePreset completed: ${presetId}`);
134
- },
135
- updatePreset: async (uid, data) => {
136
- console.log(`[Controller] 🔄 updatePreset called: uid=${uid}`, data);
137
- await new Promise(resolve => setTimeout(resolve, 300));
138
- console.log(`[Controller] 🔄 updatePreset completed:`, data.name);
139
- },
140
- createEditorConfig: async (uid, payload) => {
141
- console.log(`[Controller] ⚙️ createEditorConfig called: uid=${uid}`, payload);
142
- await new Promise(resolve => setTimeout(resolve, 300));
143
- console.log(`[Controller] ⚙️ createEditorConfig completed for gallery_id=${payload.gallery_id}, task_id=${payload.task_id}`);
144
- },
145
- getEditorHistory: async (uid, imageId) => {
146
- console.log(`[Controller] 📚 getEditorHistory called: uid=${uid}, imageId=${imageId}`);
147
- await new Promise(resolve => setTimeout(resolve, 200));
148
- const result = {
149
- current_task_id: "",
150
- history: []
151
- };
152
- console.log(`[Controller] 📚 getEditorHistory returning empty history for demo:`, result);
153
- return result;
154
- },
155
- getGalleryUpdateTimestamp: async (uid, eventId) => {
156
- console.log(`[Controller] ⏰ getGalleryUpdateTimestamp called: uid=${uid}, eventId=${eventId}`);
157
- await new Promise(resolve => setTimeout(resolve, 100));
158
- const result = {
159
- gallery: []
160
- };
161
- console.log(`[Controller] ⏰ getGalleryUpdateTimestamp returning:`, result);
162
- return result;
163
- },
164
- setHistoryIndex: async (uid, imageId, taskId) => {
165
- console.log(`[Controller] 📍 setHistoryIndex called: uid=${uid}, imageId=${imageId}, taskId=${taskId}`);
166
- await new Promise(resolve => setTimeout(resolve, 100));
167
- console.log(`[Controller] 📍 setHistoryIndex completed - Set history index for image ${imageId} to task ${taskId}`);
168
- },
169
- };
170
- };
171
- const AdjustmentControls = ({ label, onDecreaseMax, onDecrease, onIncrease, onIncreaseMax, disabled = false }) => (_jsxs(Box, { mb: 2, children: [_jsx(Typography, { variant: "body2", gutterBottom: true, children: label }), _jsxs(ButtonGroup, { size: "small", variant: "outlined", children: [_jsx(Button, { onClick: onDecreaseMax, disabled: disabled, children: "--" }), _jsx(Button, { onClick: onDecrease, disabled: disabled, children: "-" }), _jsx(Button, { onClick: onIncrease, disabled: disabled, children: "+" }), _jsx(Button, { onClick: onIncreaseMax, disabled: disabled, children: "++" })] })] }));
172
- const ImageCard = React.memo(({ image, onToggleSelection }) => {
173
- const [isVisible, setIsVisible] = useState(false);
174
- const [shouldLoadImage, setShouldLoadImage] = useState(false);
175
- const [isInView, setIsInView] = useState(false);
176
- const cardRef = useRef(null);
177
- const abortControllerRef = useRef(null);
178
- useEffect(() => {
179
- const visibilityObserver = new IntersectionObserver(([entry]) => {
180
- if (entry.isIntersecting) {
181
- setIsVisible(true);
182
- setIsInView(true);
183
- // Delay image loading slightly to ensure smooth scrolling
184
- setTimeout(() => setShouldLoadImage(true), 100);
185
- // Don't disconnect here as we want to track when it goes out of view
186
- }
187
- else {
188
- setIsInView(false);
189
- // Cancel processing when going out of view
190
- if (abortControllerRef.current) {
191
- console.debug(`[ImageCard] Cancelling processing for image ${image.key} - out of view`);
192
- abortControllerRef.current.abort();
193
- }
194
- }
195
- }, {
196
- threshold: 0.1, // Trigger when 10% of the element is visible
197
- rootMargin: '100px', // Start loading 100px before the element comes into view
198
- });
199
- if (cardRef.current) {
200
- visibilityObserver.observe(cardRef.current);
201
- }
202
- return () => {
203
- visibilityObserver.disconnect();
204
- // Cancel any ongoing processing when component unmounts
205
- if (abortControllerRef.current) {
206
- abortControllerRef.current.abort();
207
- }
208
- };
209
- }, [image.key]);
210
- // Track adjustments changes for debugging
211
- useEffect(() => {
212
- console.log("[ImageCard] Adjustments changed for image", image.key, ":", image.adjustments);
213
- }, [image.adjustments, image.key]);
214
- // Create a key that changes when adjustments change to force abort controller recreation
215
- const adjustmentsKey = useMemo(() => {
216
- return JSON.stringify(image.adjustments);
217
- }, [image.adjustments]);
218
- // Store the current abort signal in a ref to prevent it from changing on every render
219
- const currentAbortSignalRef = useRef();
220
- const isInitializedRef = useRef(false);
221
- // Much more conservative abort controller management - only recreate when absolutely necessary
222
- useEffect(() => {
223
- if (shouldLoadImage && isInView) {
224
- // Only create abort controller if we don't have one OR if this is the first time
225
- if (!abortControllerRef.current || !isInitializedRef.current) {
226
- console.debug(`[ImageCard] Creating initial abort controller for image ${image.key}`);
227
- if (abortControllerRef.current) {
228
- abortControllerRef.current.abort();
229
- }
230
- abortControllerRef.current = new AbortController();
231
- currentAbortSignalRef.current = abortControllerRef.current.signal;
232
- isInitializedRef.current = true;
233
- }
234
- // For adjustment changes, don't recreate the controller - let useImageProcessor handle it
235
- }
236
- else {
237
- // Only clean up when not visible or not loading
238
- if (abortControllerRef.current && isInitializedRef.current) {
239
- console.debug(`[ImageCard] Cleaning up abort controller for image ${image.key} - not visible or not loading`);
240
- abortControllerRef.current.abort();
241
- abortControllerRef.current = null;
242
- currentAbortSignalRef.current = undefined;
243
- isInitializedRef.current = false;
244
- }
245
- }
246
- return () => {
247
- if (abortControllerRef.current) {
248
- abortControllerRef.current.abort();
249
- abortControllerRef.current = null;
250
- currentAbortSignalRef.current = undefined;
251
- isInitializedRef.current = false;
252
- }
253
- };
254
- }, [shouldLoadImage, isInView, image.key]); // Remove adjustmentsKey from dependencies
255
- // Separate effect to handle adjustment changes without recreating abort controller
256
- useEffect(() => {
257
- if (shouldLoadImage && isInView && isInitializedRef.current) {
258
- console.debug(`[ImageCard] Adjustments changed for image ${image.key} - letting useImageProcessor handle the change`);
259
- }
260
- }, [adjustmentsKey, shouldLoadImage, isInView, image.key]);
261
- // Memoize the useImageProcessor call to prevent unnecessary hook calls
262
- const imageProcessorParams = useMemo(() => ({
263
- photoId: image.key,
264
- photoSrc: image.src,
265
- adjustments: image.adjustments,
266
- enableEditor: shouldLoadImage && isInView, // Only enable when we should load the image AND it's in view
267
- abortSignal: currentAbortSignalRef.current // Use stable abort signal
268
- }), [image.key, image.src, image.adjustments, shouldLoadImage, isInView]);
269
- // Only call useImageProcessor when the card is visible AND we should load the image AND it's in view
270
- const { processedImageSrc, isProcessingComplete, cancelProcessing, isProcessing } = useImageProcessor(imageProcessorParams);
271
- // Cancel processing when card goes out of view
272
- useEffect(() => {
273
- if (!isInView && isProcessing) {
274
- console.debug(`[ImageCard] Cancelling processing for image ${image.key} - no longer in view`);
275
- cancelProcessing();
276
- }
277
- }, [isInView, isProcessing, cancelProcessing, image.key]);
278
- // Memoize the toggle selection handler to prevent unnecessary re-renders
279
- const handleToggleSelection = useMemo(() => {
280
- return () => onToggleSelection(image.key);
281
- }, [onToggleSelection, image.key]);
282
- const handleCheckboxClick = useMemo(() => {
283
- return (e) => {
284
- e.stopPropagation();
285
- onToggleSelection(image.key);
286
- };
287
- }, [onToggleSelection, image.key]);
288
- // Create placeholder content for non-visible cards
289
- const renderPlaceholder = () => (_jsxs(Box, { sx: {
290
- height: 200,
291
- backgroundColor: 'grey.100',
292
- display: 'flex',
293
- alignItems: 'center',
294
- justifyContent: 'center',
295
- flexDirection: 'column'
296
- }, children: [_jsx(CircularProgress, { size: 24, sx: { mb: 1 } }), _jsx(Typography, { variant: "caption", color: "text.secondary", children: "Loading..." })] }));
297
- // Create processing indicator
298
- const renderProcessingOverlay = () => {
299
- return isProcessing && shouldLoadImage && isInView ? (_jsxs(Box, { sx: {
300
- position: 'absolute',
301
- top: 8,
302
- right: 8,
303
- backgroundColor: 'rgba(0, 0, 0, 0.8)',
304
- color: 'white',
305
- borderRadius: 1,
306
- px: 1,
307
- py: 0.5,
308
- display: 'flex',
309
- alignItems: 'center',
310
- gap: 0.5,
311
- zIndex: 1,
312
- maxWidth: '120px'
313
- }, children: [_jsx(CircularProgress, { size: 12, color: "inherit" }), _jsx(Typography, { variant: "caption", children: "Processing" })] })) : null;
314
- };
315
- return (_jsxs(Card, { ref: cardRef, sx: {
316
- border: image.isSelected ? 2 : 1,
317
- borderColor: image.isSelected ? 'primary.main' : 'divider',
318
- cursor: 'pointer',
319
- transition: 'all 0.2s',
320
- position: 'relative',
321
- opacity: 1,
322
- '&:hover': {
323
- transform: 'translateY(-2px)',
324
- boxShadow: 2,
325
- }
326
- }, onClick: handleToggleSelection, children: [shouldLoadImage ? (_jsxs(Box, { sx: { position: 'relative', width: '100%', height: '200px' }, children: [_jsx("img", { id: "image-card-media", src: processedImageSrc, alt: "", style: {
327
- width: '100%',
328
- height: '200px',
329
- objectFit: 'cover',
330
- opacity: 1,
331
- display: 'block'
332
- }, loading: "lazy" }), renderProcessingOverlay()] })) : (renderPlaceholder()), _jsx(CardContent, { sx: { pb: 1 }, children: _jsxs(Box, { display: "flex", alignItems: "center", justifyContent: "space-between", children: [_jsxs(Typography, { variant: "body2", color: "text.secondary", children: ["Image ", image.key, !shouldLoadImage && (_jsx(Typography, { component: "span", variant: "caption", sx: { ml: 1 }, children: "(waiting...)" })), !isInView && shouldLoadImage && (_jsx(Typography, { component: "span", variant: "caption", sx: { ml: 1, color: 'warning.main' }, children: "(paused)" }))] }), _jsx(Checkbox, { checked: image.isSelected, size: "small", onClick: handleCheckboxClick })] }) })] }));
333
- }, (prevProps, nextProps) => {
334
- // Custom comparison function for React.memo - be more strict to prevent unnecessary re-renders
335
- const prevAdj = prevProps.image.adjustments || {};
336
- const nextAdj = nextProps.image.adjustments || {};
337
- // Only re-render if key properties actually change
338
- const shouldUpdate = (prevProps.image.key !== nextProps.image.key ||
339
- prevProps.image.isSelected !== nextProps.image.isSelected ||
340
- prevProps.image.src !== nextProps.image.src ||
341
- // Compare individual adjustment properties to avoid JSON.stringify issues
342
- prevAdj.temperature !== nextAdj.temperature ||
343
- prevAdj.tint !== nextAdj.tint ||
344
- prevAdj.exposure !== nextAdj.exposure ||
345
- prevAdj.contrast !== nextAdj.contrast ||
346
- prevAdj.clarity !== nextAdj.clarity ||
347
- prevAdj.vibrance !== nextAdj.vibrance ||
348
- prevAdj.saturation !== nextAdj.saturation ||
349
- prevAdj.highlights !== nextAdj.highlights ||
350
- prevAdj.shadows !== nextAdj.shadows ||
351
- prevAdj.whites !== nextAdj.whites ||
352
- prevAdj.blacks !== nextAdj.blacks ||
353
- prevAdj.sharpness !== nextAdj.sharpness);
354
- // Return false to re-render, true to skip re-render
355
- return !shouldUpdate;
356
- });
357
- export const HonchoEditorBulkDemo = () => {
358
- const [controller] = useState(() => createMockController());
359
- const [showAdjustments, setShowAdjustments] = useState(false);
360
- const { imageData, isLoading, error, selectedIds, hasMore, selectedBulkPreset, presets, // Add presets
361
- activePreset, // Add activePreset
362
- handleToggleImageSelection, handleLoadMore, handleRefresh, handleSelectBulkPreset, handleBackCallbackBulk,
363
- // Temperature adjustments
364
- handleBulkTempDecreaseMax, handleBulkTempDecrease, handleBulkTempIncrease, handleBulkTempIncreaseMax,
365
- // Tint adjustments
366
- handleBulkTintDecreaseMax, handleBulkTintDecrease, handleBulkTintIncrease, handleBulkTintIncreaseMax,
367
- // Exposure adjustments
368
- handleBulkExposureDecreaseMax, handleBulkExposureDecrease, handleBulkExposureIncrease, handleBulkExposureIncreaseMax,
369
- // Contrast adjustments
370
- handleBulkContrastDecreaseMax, handleBulkContrastDecrease, handleBulkContrastIncrease, handleBulkContrastIncreaseMax,
371
- // Clarity adjustments
372
- handleBulkClarityDecreaseMax, handleBulkClarityDecrease, handleBulkClarityIncrease, handleBulkClarityIncreaseMax,
373
- // History actions
374
- handleUndo, handleRedo, handleReset, historyInfo, } = useHonchoEditorBulk(controller, 'demo-event', 'demo-user');
375
- const selectedCount = selectedIds.length;
376
- const totalCount = imageData.length;
377
- return (_jsxs(Container, { maxWidth: "xl", sx: { py: 4 }, children: [_jsx(Typography, { variant: "h3", gutterBottom: true, align: "center", children: "Honcho Editor Bulk Demo" }), _jsx(Typography, { variant: "subtitle1", align: "center", color: "text.secondary", gutterBottom: true, children: "This demo shows the useHonchoEditorBulk hook with 1,277 mock images from picsum.dev" }), error && (_jsx(Alert, { severity: "error", sx: { mb: 3 }, children: error })), _jsx(Paper, { sx: { p: 3, mb: 3 }, children: _jsxs(Stack, { direction: "row", spacing: 2, alignItems: "center", flexWrap: "wrap", children: [_jsx(Button, { variant: "contained", onClick: handleRefresh, disabled: isLoading, children: isLoading ? _jsx(CircularProgress, { size: 20 }) : 'Refresh Images' }), _jsxs(Button, { variant: "outlined", onClick: () => setShowAdjustments(!showAdjustments), children: [showAdjustments ? 'Hide' : 'Show', " Adjustments"] }), _jsx(Button, { variant: "outlined", onClick: () => {
378
- // Select all images on current page
379
- imageData.forEach(image => {
380
- if (!image.isSelected) {
381
- handleToggleImageSelection(image.key);
382
- }
383
- });
384
- }, disabled: isLoading || imageData.length === 0, children: "Select All" }), _jsx(Button, { variant: "outlined", onClick: () => {
385
- // Deselect all images
386
- imageData.forEach(image => {
387
- if (image.isSelected) {
388
- handleToggleImageSelection(image.key);
389
- }
390
- });
391
- }, disabled: isLoading || selectedCount === 0, children: "Clear Selection" }), _jsx(Button, { variant: "outlined", onClick: () => {
392
- console.log('Debug Info:', {
393
- selectedIds,
394
- selectedCount,
395
- showAdjustments,
396
- presets: presets.map(p => ({ id: p.id, name: p.name })),
397
- selectedBulkPreset,
398
- activePreset: activePreset ? { id: activePreset.id, name: activePreset.name } : null,
399
- imageData: imageData.map(img => ({ key: img.key, isSelected: img.isSelected }))
400
- });
401
- }, children: "Debug Selection" }), _jsx(Button, { variant: "outlined", onClick: handleBackCallbackBulk, children: "Back" }), _jsx(Chip, { label: `${selectedCount} of ${totalCount} selected`, color: selectedCount > 0 ? "primary" : "default" }), hasMore && (_jsx(Button, { variant: "outlined", onClick: handleLoadMore, disabled: isLoading, children: "Load More" })), _jsxs(FormControl, { size: "small", sx: { minWidth: 150 }, children: [_jsx(InputLabel, { children: "Bulk Preset" }), _jsxs(Select, { value: selectedBulkPreset, onChange: handleSelectBulkPreset, label: "Bulk Preset", displayEmpty: true, children: [_jsx(MenuItem, { value: "", children: _jsx("em", { children: "No Preset" }) }), presets.map((preset) => (_jsxs(MenuItem, { value: preset.id, children: [preset.name, activePreset?.id === preset.id && ' ✓'] }, preset.id)))] })] }), _jsxs(Typography, { variant: "body2", sx: { ml: 2, opacity: 0.7 }, children: ["Active: ", activePreset ? activePreset.name : 'None', " | Presets: ", presets.length] })] }) }), showAdjustments && (_jsxs(Paper, { sx: { p: 3, mb: 3 }, children: [_jsxs(Stack, { direction: "row", justifyContent: "space-between", alignItems: "center", sx: { mb: 2 }, children: [_jsxs(Typography, { variant: "h6", children: ["Bulk Adjustments (", selectedCount, " images selected)"] }), _jsxs(Stack, { direction: "row", spacing: 1, children: [_jsx(Button, { variant: "outlined", size: "small", onClick: handleUndo, disabled: !historyInfo.canUndo, sx: { minWidth: 80 }, children: "Undo" }), _jsx(Button, { variant: "outlined", size: "small", onClick: handleRedo, disabled: !historyInfo.canRedo, sx: { minWidth: 80 }, children: "Redo" }), _jsx(Button, { variant: "outlined", size: "small", onClick: () => handleReset(), disabled: selectedCount === 0, color: "warning", sx: { minWidth: 80 }, children: "Reset" })] })] }), selectedCount === 0 && (_jsx(Typography, { variant: "body2", color: "text.secondary", sx: { mb: 2 }, children: "Select one or more images below to enable bulk adjustments" })), _jsxs(Typography, { variant: "body2", color: "text.secondary", sx: { mb: 2 }, children: ["History: ", historyInfo.currentIndex + 1, "/", historyInfo.totalStates, " states | Can Undo: ", historyInfo.canUndo ? 'Yes' : 'No', " | Can Redo: ", historyInfo.canRedo ? 'Yes' : 'No'] }), _jsxs(Grid, { container: true, spacing: 3, children: [_jsxs(Grid, { item: true, xs: 12, md: 3, children: [_jsx(Typography, { variant: "subtitle2", gutterBottom: true, children: "Color" }), _jsx(AdjustmentControls, { label: "Temperature", onDecreaseMax: handleBulkTempDecreaseMax, onDecrease: handleBulkTempDecrease, onIncrease: handleBulkTempIncrease, onIncreaseMax: handleBulkTempIncreaseMax, disabled: selectedCount === 0 }), _jsx(AdjustmentControls, { label: "Tint", onDecreaseMax: handleBulkTintDecreaseMax, onDecrease: handleBulkTintDecrease, onIncrease: handleBulkTintIncrease, onIncreaseMax: handleBulkTintIncreaseMax, disabled: selectedCount === 0 })] }), _jsxs(Grid, { item: true, xs: 12, md: 3, children: [_jsx(Typography, { variant: "subtitle2", gutterBottom: true, children: "Light" }), _jsx(AdjustmentControls, { label: "Exposure", onDecreaseMax: handleBulkExposureDecreaseMax, onDecrease: handleBulkExposureDecrease, onIncrease: handleBulkExposureIncrease, onIncreaseMax: handleBulkExposureIncreaseMax, disabled: selectedCount === 0 }), _jsx(AdjustmentControls, { label: "Contrast", onDecreaseMax: handleBulkContrastDecreaseMax, onDecrease: handleBulkContrastDecrease, onIncrease: handleBulkContrastIncrease, onIncreaseMax: handleBulkContrastIncreaseMax, disabled: selectedCount === 0 })] }), _jsxs(Grid, { item: true, xs: 12, md: 3, children: [_jsx(Typography, { variant: "subtitle2", gutterBottom: true, children: "Details" }), _jsx(AdjustmentControls, { label: "Clarity", onDecreaseMax: handleBulkClarityDecreaseMax, onDecrease: handleBulkClarityDecrease, onIncrease: handleBulkClarityIncrease, onIncreaseMax: handleBulkClarityIncreaseMax, disabled: selectedCount === 0 })] })] })] })), _jsx(Grid, { container: true, spacing: 2, children: imageData.map((image) => (_jsx(Grid, { item: true, xs: 12, sm: 6, md: 4, lg: 3, children: _jsx(ImageCard, { image: image, onToggleSelection: handleToggleImageSelection }) }, image.key))) }), isLoading && imageData.length === 0 && (_jsx(Box, { display: "flex", justifyContent: "center", py: 4, children: _jsx(CircularProgress, {}) })), _jsxs(Box, { mt: 4, children: [_jsx(Divider, {}), _jsx(Typography, { variant: "body2", color: "text.secondary", align: "center", sx: { mt: 2 }, children: "Demo Notes: This uses mock data and simulated API calls. Select images and try the bulk adjustment controls above." })] })] }));
402
- };
403
- // Add debugging function to global scope for manual testing
404
- if (typeof window !== 'undefined') {
405
- window.testPresetSelection = () => {
406
- console.log("🧪 Manual preset test - this would normally be called by the UI");
407
- console.log("🧪 To test properly, use the actual preset dropdown in the UI above");
408
- };
409
- }
410
- export default HonchoEditorBulkDemo;