@qwanyx/carousel 0.1.2 → 0.1.3

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/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import React, { CSSProperties, ReactNode } from 'react';
1
+ import React$1, { CSSProperties, ReactNode } from 'react';
2
2
  import * as react_jsx_runtime from 'react/jsx-runtime';
3
3
 
4
4
  /**
@@ -272,6 +272,18 @@ interface CarouselProps {
272
272
  onThumbnailReorder?: (fromIndex: number, toIndex: number) => void;
273
273
  /** Called when delete button is clicked on a thumbnail */
274
274
  onThumbnailDelete?: (index: number) => void;
275
+ /** Called when an image is edited and saved (enables editing on double-click) */
276
+ onImageEdit?: (index: number, blob: Blob) => void;
277
+ /** Default aspect ratio for the image editor */
278
+ editorAspectRatio?: '4/3' | '16/9' | '1/1' | '3/4' | '9/16' | 'free';
279
+ /** ImageEditor component from @qwanyx/stack (pass it to enable editing) */
280
+ ImageEditor?: React.ComponentType<{
281
+ src: string;
282
+ onSave: (blob: Blob) => void;
283
+ onCancel?: () => void;
284
+ aspectRatio?: '4/3' | '16/9' | '1/1' | '3/4' | '9/16' | 'free';
285
+ theme?: 'light' | 'dark';
286
+ }>;
275
287
  }
276
288
  /**
277
289
  * Carousel ref methods
@@ -305,7 +317,7 @@ declare function createImageSlide(id: string, src: string, options?: {
305
317
  /**
306
318
  * Qwanyx Carousel - Universal slide/presentation engine
307
319
  */
308
- declare const Carousel: React.ForwardRefExoticComponent<CarouselProps & React.RefAttributes<CarouselRef>>;
320
+ declare const Carousel: React$1.ForwardRefExoticComponent<CarouselProps & React$1.RefAttributes<CarouselRef>>;
309
321
 
310
322
  interface SlideRendererProps {
311
323
  slide: Slide;
@@ -386,7 +398,7 @@ declare const createSlide: {
386
398
  component: (id: string, render: (props: {
387
399
  isActive: boolean;
388
400
  slideIndex: number;
389
- }) => React.ReactNode, options?: Partial<Omit<Slide, "id" | "layers">>) => Slide;
401
+ }) => React$1.ReactNode, options?: Partial<Omit<Slide, "id" | "layers">>) => Slide;
390
402
  /**
391
403
  * Create a slide to display a markdown file
392
404
  */
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import React, { CSSProperties, ReactNode } from 'react';
1
+ import React$1, { CSSProperties, ReactNode } from 'react';
2
2
  import * as react_jsx_runtime from 'react/jsx-runtime';
3
3
 
4
4
  /**
@@ -272,6 +272,18 @@ interface CarouselProps {
272
272
  onThumbnailReorder?: (fromIndex: number, toIndex: number) => void;
273
273
  /** Called when delete button is clicked on a thumbnail */
274
274
  onThumbnailDelete?: (index: number) => void;
275
+ /** Called when an image is edited and saved (enables editing on double-click) */
276
+ onImageEdit?: (index: number, blob: Blob) => void;
277
+ /** Default aspect ratio for the image editor */
278
+ editorAspectRatio?: '4/3' | '16/9' | '1/1' | '3/4' | '9/16' | 'free';
279
+ /** ImageEditor component from @qwanyx/stack (pass it to enable editing) */
280
+ ImageEditor?: React.ComponentType<{
281
+ src: string;
282
+ onSave: (blob: Blob) => void;
283
+ onCancel?: () => void;
284
+ aspectRatio?: '4/3' | '16/9' | '1/1' | '3/4' | '9/16' | 'free';
285
+ theme?: 'light' | 'dark';
286
+ }>;
275
287
  }
276
288
  /**
277
289
  * Carousel ref methods
@@ -305,7 +317,7 @@ declare function createImageSlide(id: string, src: string, options?: {
305
317
  /**
306
318
  * Qwanyx Carousel - Universal slide/presentation engine
307
319
  */
308
- declare const Carousel: React.ForwardRefExoticComponent<CarouselProps & React.RefAttributes<CarouselRef>>;
320
+ declare const Carousel: React$1.ForwardRefExoticComponent<CarouselProps & React$1.RefAttributes<CarouselRef>>;
309
321
 
310
322
  interface SlideRendererProps {
311
323
  slide: Slide;
@@ -386,7 +398,7 @@ declare const createSlide: {
386
398
  component: (id: string, render: (props: {
387
399
  isActive: boolean;
388
400
  slideIndex: number;
389
- }) => React.ReactNode, options?: Partial<Omit<Slide, "id" | "layers">>) => Slide;
401
+ }) => React$1.ReactNode, options?: Partial<Omit<Slide, "id" | "layers">>) => Slide;
390
402
  /**
391
403
  * Create a slide to display a markdown file
392
404
  */
package/dist/index.js CHANGED
@@ -788,12 +788,37 @@ var Carousel = (0, import_react4.forwardRef)(
788
788
  onCopySlide,
789
789
  // Thumbnail callbacks
790
790
  onThumbnailReorder,
791
- onThumbnailDelete
791
+ onThumbnailDelete,
792
+ // Image editing
793
+ onImageEdit,
794
+ editorAspectRatio = "4/3",
795
+ ImageEditor
792
796
  }, ref) => {
793
797
  const containerRef = (0, import_react4.useRef)(null);
794
798
  const [isFullscreen, setIsFullscreen] = (0, import_react4.useState)(false);
795
799
  const [touchStart, setTouchStart] = (0, import_react4.useState)(null);
796
800
  const [copiedState, setCopiedState] = (0, import_react4.useState)(null);
801
+ const [editingIndex, setEditingIndex] = (0, import_react4.useState)(null);
802
+ const canEdit = ImageEditor !== void 0 && onImageEdit !== void 0;
803
+ const handleSlideDoubleClick = (0, import_react4.useCallback)((index) => {
804
+ if (!canEdit) return;
805
+ setEditingIndex(index);
806
+ }, [canEdit]);
807
+ const handleImageSave = (0, import_react4.useCallback)((blob) => {
808
+ if (editingIndex !== null && onImageEdit) {
809
+ onImageEdit(editingIndex, blob);
810
+ }
811
+ setEditingIndex(null);
812
+ }, [editingIndex, onImageEdit]);
813
+ const handleEditorCancel = (0, import_react4.useCallback)(() => {
814
+ setEditingIndex(null);
815
+ }, []);
816
+ const getEditingImageUrl = (0, import_react4.useCallback)(() => {
817
+ if (editingIndex === null) return void 0;
818
+ const slide = slides[editingIndex];
819
+ if (!slide) return void 0;
820
+ return getSlideUrl ? getSlideUrl(slide) : extractSlideUrl(slide);
821
+ }, [editingIndex, slides, getSlideUrl]);
797
822
  const copyToClipboard = (0, import_react4.useCallback)(async (text, type) => {
798
823
  try {
799
824
  await navigator.clipboard.writeText(text);
@@ -1009,8 +1034,10 @@ var Carousel = (0, import_react4.forwardRef)(
1009
1034
  left: 0,
1010
1035
  opacity: transition === "fade" ? index === currentIndex ? 1 : 0 : 1,
1011
1036
  transition: `opacity ${transitionDuration}ms ease-in-out`,
1012
- pointerEvents: index === currentIndex ? "auto" : "none"
1037
+ pointerEvents: index === currentIndex ? "auto" : "none",
1038
+ cursor: canEdit ? "pointer" : "default"
1013
1039
  },
1040
+ onDoubleClick: () => handleSlideDoubleClick(index),
1014
1041
  children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SlideRenderer, { slide, isActive: index === currentIndex, slideIndex: index })
1015
1042
  },
1016
1043
  slide.id
@@ -1249,6 +1276,80 @@ var Carousel = (0, import_react4.forwardRef)(
1249
1276
  onDelete: onThumbnailDelete,
1250
1277
  theme: theme === "auto" ? "light" : theme
1251
1278
  }
1279
+ ),
1280
+ editingIndex !== null && ImageEditor && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1281
+ "div",
1282
+ {
1283
+ className: "qc-carousel__editor-overlay",
1284
+ style: {
1285
+ position: "fixed",
1286
+ top: 0,
1287
+ left: 0,
1288
+ right: 0,
1289
+ bottom: 0,
1290
+ zIndex: 9999,
1291
+ backgroundColor: theme === "dark" ? "#1a1a1a" : "#f5f5f5",
1292
+ display: "flex",
1293
+ flexDirection: "column"
1294
+ },
1295
+ children: [
1296
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1297
+ "div",
1298
+ {
1299
+ style: {
1300
+ padding: "12px 16px",
1301
+ display: "flex",
1302
+ alignItems: "center",
1303
+ justifyContent: "space-between",
1304
+ borderBottom: `1px solid ${theme === "dark" ? "#333" : "#ddd"}`,
1305
+ backgroundColor: theme === "dark" ? "#242424" : "#fff"
1306
+ },
1307
+ children: [
1308
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { style: {
1309
+ color: theme === "dark" ? "#fff" : "#333",
1310
+ fontWeight: 500,
1311
+ fontSize: "16px"
1312
+ }, children: [
1313
+ "Edit Image ",
1314
+ editingIndex + 1,
1315
+ " / ",
1316
+ totalSlides
1317
+ ] }),
1318
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1319
+ "button",
1320
+ {
1321
+ onClick: handleEditorCancel,
1322
+ style: {
1323
+ background: "none",
1324
+ border: "none",
1325
+ color: theme === "dark" ? "#aaa" : "#666",
1326
+ cursor: "pointer",
1327
+ fontSize: "24px",
1328
+ lineHeight: 1,
1329
+ padding: "4px"
1330
+ },
1331
+ children: "\xD7"
1332
+ }
1333
+ )
1334
+ ]
1335
+ }
1336
+ ),
1337
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { flex: 1, minHeight: 0 }, children: (() => {
1338
+ const imageUrl = getEditingImageUrl();
1339
+ if (!imageUrl) return null;
1340
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1341
+ ImageEditor,
1342
+ {
1343
+ src: imageUrl,
1344
+ onSave: handleImageSave,
1345
+ onCancel: handleEditorCancel,
1346
+ aspectRatio: editorAspectRatio,
1347
+ theme: theme === "auto" ? "dark" : theme
1348
+ }
1349
+ );
1350
+ })() })
1351
+ ]
1352
+ }
1252
1353
  )
1253
1354
  ]
1254
1355
  }
package/dist/index.mjs CHANGED
@@ -753,12 +753,37 @@ var Carousel = forwardRef(
753
753
  onCopySlide,
754
754
  // Thumbnail callbacks
755
755
  onThumbnailReorder,
756
- onThumbnailDelete
756
+ onThumbnailDelete,
757
+ // Image editing
758
+ onImageEdit,
759
+ editorAspectRatio = "4/3",
760
+ ImageEditor
757
761
  }, ref) => {
758
762
  const containerRef = useRef3(null);
759
763
  const [isFullscreen, setIsFullscreen] = useState3(false);
760
764
  const [touchStart, setTouchStart] = useState3(null);
761
765
  const [copiedState, setCopiedState] = useState3(null);
766
+ const [editingIndex, setEditingIndex] = useState3(null);
767
+ const canEdit = ImageEditor !== void 0 && onImageEdit !== void 0;
768
+ const handleSlideDoubleClick = useCallback2((index) => {
769
+ if (!canEdit) return;
770
+ setEditingIndex(index);
771
+ }, [canEdit]);
772
+ const handleImageSave = useCallback2((blob) => {
773
+ if (editingIndex !== null && onImageEdit) {
774
+ onImageEdit(editingIndex, blob);
775
+ }
776
+ setEditingIndex(null);
777
+ }, [editingIndex, onImageEdit]);
778
+ const handleEditorCancel = useCallback2(() => {
779
+ setEditingIndex(null);
780
+ }, []);
781
+ const getEditingImageUrl = useCallback2(() => {
782
+ if (editingIndex === null) return void 0;
783
+ const slide = slides[editingIndex];
784
+ if (!slide) return void 0;
785
+ return getSlideUrl ? getSlideUrl(slide) : extractSlideUrl(slide);
786
+ }, [editingIndex, slides, getSlideUrl]);
762
787
  const copyToClipboard = useCallback2(async (text, type) => {
763
788
  try {
764
789
  await navigator.clipboard.writeText(text);
@@ -974,8 +999,10 @@ var Carousel = forwardRef(
974
999
  left: 0,
975
1000
  opacity: transition === "fade" ? index === currentIndex ? 1 : 0 : 1,
976
1001
  transition: `opacity ${transitionDuration}ms ease-in-out`,
977
- pointerEvents: index === currentIndex ? "auto" : "none"
1002
+ pointerEvents: index === currentIndex ? "auto" : "none",
1003
+ cursor: canEdit ? "pointer" : "default"
978
1004
  },
1005
+ onDoubleClick: () => handleSlideDoubleClick(index),
979
1006
  children: /* @__PURE__ */ jsx3(SlideRenderer, { slide, isActive: index === currentIndex, slideIndex: index })
980
1007
  },
981
1008
  slide.id
@@ -1214,6 +1241,80 @@ var Carousel = forwardRef(
1214
1241
  onDelete: onThumbnailDelete,
1215
1242
  theme: theme === "auto" ? "light" : theme
1216
1243
  }
1244
+ ),
1245
+ editingIndex !== null && ImageEditor && /* @__PURE__ */ jsxs3(
1246
+ "div",
1247
+ {
1248
+ className: "qc-carousel__editor-overlay",
1249
+ style: {
1250
+ position: "fixed",
1251
+ top: 0,
1252
+ left: 0,
1253
+ right: 0,
1254
+ bottom: 0,
1255
+ zIndex: 9999,
1256
+ backgroundColor: theme === "dark" ? "#1a1a1a" : "#f5f5f5",
1257
+ display: "flex",
1258
+ flexDirection: "column"
1259
+ },
1260
+ children: [
1261
+ /* @__PURE__ */ jsxs3(
1262
+ "div",
1263
+ {
1264
+ style: {
1265
+ padding: "12px 16px",
1266
+ display: "flex",
1267
+ alignItems: "center",
1268
+ justifyContent: "space-between",
1269
+ borderBottom: `1px solid ${theme === "dark" ? "#333" : "#ddd"}`,
1270
+ backgroundColor: theme === "dark" ? "#242424" : "#fff"
1271
+ },
1272
+ children: [
1273
+ /* @__PURE__ */ jsxs3("span", { style: {
1274
+ color: theme === "dark" ? "#fff" : "#333",
1275
+ fontWeight: 500,
1276
+ fontSize: "16px"
1277
+ }, children: [
1278
+ "Edit Image ",
1279
+ editingIndex + 1,
1280
+ " / ",
1281
+ totalSlides
1282
+ ] }),
1283
+ /* @__PURE__ */ jsx3(
1284
+ "button",
1285
+ {
1286
+ onClick: handleEditorCancel,
1287
+ style: {
1288
+ background: "none",
1289
+ border: "none",
1290
+ color: theme === "dark" ? "#aaa" : "#666",
1291
+ cursor: "pointer",
1292
+ fontSize: "24px",
1293
+ lineHeight: 1,
1294
+ padding: "4px"
1295
+ },
1296
+ children: "\xD7"
1297
+ }
1298
+ )
1299
+ ]
1300
+ }
1301
+ ),
1302
+ /* @__PURE__ */ jsx3("div", { style: { flex: 1, minHeight: 0 }, children: (() => {
1303
+ const imageUrl = getEditingImageUrl();
1304
+ if (!imageUrl) return null;
1305
+ return /* @__PURE__ */ jsx3(
1306
+ ImageEditor,
1307
+ {
1308
+ src: imageUrl,
1309
+ onSave: handleImageSave,
1310
+ onCancel: handleEditorCancel,
1311
+ aspectRatio: editorAspectRatio,
1312
+ theme: theme === "auto" ? "dark" : theme
1313
+ }
1314
+ );
1315
+ })() })
1316
+ ]
1317
+ }
1217
1318
  )
1218
1319
  ]
1219
1320
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qwanyx/carousel",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -27,7 +27,13 @@
27
27
  },
28
28
  "peerDependencies": {
29
29
  "react": ">=18.0.0",
30
- "react-dom": ">=18.0.0"
30
+ "react-dom": ">=18.0.0",
31
+ "@qwanyx/stack": ">=0.2.71"
32
+ },
33
+ "peerDependenciesMeta": {
34
+ "@qwanyx/stack": {
35
+ "optional": true
36
+ }
31
37
  },
32
38
  "devDependencies": {
33
39
  "@types/react": "^18.2.0",