@qwanyx/carousel 0.1.2 → 0.1.4

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,11 @@ 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",
1039
+ zIndex: index === currentIndex ? 1 : 0
1013
1040
  },
1041
+ onDoubleClick: () => handleSlideDoubleClick(index),
1014
1042
  children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SlideRenderer, { slide, isActive: index === currentIndex, slideIndex: index })
1015
1043
  },
1016
1044
  slide.id
@@ -1249,6 +1277,80 @@ var Carousel = (0, import_react4.forwardRef)(
1249
1277
  onDelete: onThumbnailDelete,
1250
1278
  theme: theme === "auto" ? "light" : theme
1251
1279
  }
1280
+ ),
1281
+ editingIndex !== null && ImageEditor && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1282
+ "div",
1283
+ {
1284
+ className: "qc-carousel__editor-overlay",
1285
+ style: {
1286
+ position: "fixed",
1287
+ top: 0,
1288
+ left: 0,
1289
+ right: 0,
1290
+ bottom: 0,
1291
+ zIndex: 9999,
1292
+ backgroundColor: theme === "dark" ? "#1a1a1a" : "#f5f5f5",
1293
+ display: "flex",
1294
+ flexDirection: "column"
1295
+ },
1296
+ children: [
1297
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1298
+ "div",
1299
+ {
1300
+ style: {
1301
+ padding: "12px 16px",
1302
+ display: "flex",
1303
+ alignItems: "center",
1304
+ justifyContent: "space-between",
1305
+ borderBottom: `1px solid ${theme === "dark" ? "#333" : "#ddd"}`,
1306
+ backgroundColor: theme === "dark" ? "#242424" : "#fff"
1307
+ },
1308
+ children: [
1309
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { style: {
1310
+ color: theme === "dark" ? "#fff" : "#333",
1311
+ fontWeight: 500,
1312
+ fontSize: "16px"
1313
+ }, children: [
1314
+ "Edit Image ",
1315
+ editingIndex + 1,
1316
+ " / ",
1317
+ totalSlides
1318
+ ] }),
1319
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1320
+ "button",
1321
+ {
1322
+ onClick: handleEditorCancel,
1323
+ style: {
1324
+ background: "none",
1325
+ border: "none",
1326
+ color: theme === "dark" ? "#aaa" : "#666",
1327
+ cursor: "pointer",
1328
+ fontSize: "24px",
1329
+ lineHeight: 1,
1330
+ padding: "4px"
1331
+ },
1332
+ children: "\xD7"
1333
+ }
1334
+ )
1335
+ ]
1336
+ }
1337
+ ),
1338
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { flex: 1, minHeight: 0 }, children: (() => {
1339
+ const imageUrl = getEditingImageUrl();
1340
+ if (!imageUrl) return null;
1341
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1342
+ ImageEditor,
1343
+ {
1344
+ src: imageUrl,
1345
+ onSave: handleImageSave,
1346
+ onCancel: handleEditorCancel,
1347
+ aspectRatio: editorAspectRatio,
1348
+ theme: theme === "auto" ? "dark" : theme
1349
+ }
1350
+ );
1351
+ })() })
1352
+ ]
1353
+ }
1252
1354
  )
1253
1355
  ]
1254
1356
  }
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,11 @@ 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",
1004
+ zIndex: index === currentIndex ? 1 : 0
978
1005
  },
1006
+ onDoubleClick: () => handleSlideDoubleClick(index),
979
1007
  children: /* @__PURE__ */ jsx3(SlideRenderer, { slide, isActive: index === currentIndex, slideIndex: index })
980
1008
  },
981
1009
  slide.id
@@ -1214,6 +1242,80 @@ var Carousel = forwardRef(
1214
1242
  onDelete: onThumbnailDelete,
1215
1243
  theme: theme === "auto" ? "light" : theme
1216
1244
  }
1245
+ ),
1246
+ editingIndex !== null && ImageEditor && /* @__PURE__ */ jsxs3(
1247
+ "div",
1248
+ {
1249
+ className: "qc-carousel__editor-overlay",
1250
+ style: {
1251
+ position: "fixed",
1252
+ top: 0,
1253
+ left: 0,
1254
+ right: 0,
1255
+ bottom: 0,
1256
+ zIndex: 9999,
1257
+ backgroundColor: theme === "dark" ? "#1a1a1a" : "#f5f5f5",
1258
+ display: "flex",
1259
+ flexDirection: "column"
1260
+ },
1261
+ children: [
1262
+ /* @__PURE__ */ jsxs3(
1263
+ "div",
1264
+ {
1265
+ style: {
1266
+ padding: "12px 16px",
1267
+ display: "flex",
1268
+ alignItems: "center",
1269
+ justifyContent: "space-between",
1270
+ borderBottom: `1px solid ${theme === "dark" ? "#333" : "#ddd"}`,
1271
+ backgroundColor: theme === "dark" ? "#242424" : "#fff"
1272
+ },
1273
+ children: [
1274
+ /* @__PURE__ */ jsxs3("span", { style: {
1275
+ color: theme === "dark" ? "#fff" : "#333",
1276
+ fontWeight: 500,
1277
+ fontSize: "16px"
1278
+ }, children: [
1279
+ "Edit Image ",
1280
+ editingIndex + 1,
1281
+ " / ",
1282
+ totalSlides
1283
+ ] }),
1284
+ /* @__PURE__ */ jsx3(
1285
+ "button",
1286
+ {
1287
+ onClick: handleEditorCancel,
1288
+ style: {
1289
+ background: "none",
1290
+ border: "none",
1291
+ color: theme === "dark" ? "#aaa" : "#666",
1292
+ cursor: "pointer",
1293
+ fontSize: "24px",
1294
+ lineHeight: 1,
1295
+ padding: "4px"
1296
+ },
1297
+ children: "\xD7"
1298
+ }
1299
+ )
1300
+ ]
1301
+ }
1302
+ ),
1303
+ /* @__PURE__ */ jsx3("div", { style: { flex: 1, minHeight: 0 }, children: (() => {
1304
+ const imageUrl = getEditingImageUrl();
1305
+ if (!imageUrl) return null;
1306
+ return /* @__PURE__ */ jsx3(
1307
+ ImageEditor,
1308
+ {
1309
+ src: imageUrl,
1310
+ onSave: handleImageSave,
1311
+ onCancel: handleEditorCancel,
1312
+ aspectRatio: editorAspectRatio,
1313
+ theme: theme === "auto" ? "dark" : theme
1314
+ }
1315
+ );
1316
+ })() })
1317
+ ]
1318
+ }
1217
1319
  )
1218
1320
  ]
1219
1321
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qwanyx/carousel",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
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",