yet-another-react-lightbox 3.6.0 → 3.8.0

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/README.md CHANGED
@@ -123,17 +123,18 @@ See [examples](https://yet-another-react-lightbox.com/examples) on the documenta
123
123
 
124
124
  ## Plugins
125
125
 
126
- Yet Another React Lightbox allows you to add optional features based on your requirements via plugins.
126
+ Yet Another React Lightbox allows you to add optional features to your project based on your requirements via plugins.
127
127
 
128
- The following plugins come bundled in the package:
128
+ The following plugins are bundled in the package:
129
129
 
130
130
  - [Captions](https://yet-another-react-lightbox.com/plugins/captions) - adds support for slide title and
131
131
  description
132
132
  - [Counter](https://yet-another-react-lightbox.com/plugins/counter) - adds slides counter
133
133
  - [Download](https://yet-another-react-lightbox.com/plugins/download) - adds download button
134
134
  - [Fullscreen](https://yet-another-react-lightbox.com/plugins/fullscreen) - adds support for fullscreen mode
135
- - [Inline](https://yet-another-react-lightbox.com/plugins/inline) - adds support for inline rendering mode
136
- - [Slideshow](https://yet-another-react-lightbox.com/plugins/slideshow) - adds slideshow autoplay feature
135
+ - [Inline](https://yet-another-react-lightbox.com/plugins/inline) - transforms the lightbox into an image carousel
136
+ - [Share](https://yet-another-react-lightbox.com/plugins/share) - adds sharing button
137
+ - [Slideshow](https://yet-another-react-lightbox.com/plugins/slideshow) - adds slideshow button
137
138
  - [Thumbnails](https://yet-another-react-lightbox.com/plugins/thumbnails) - adds thumbnails track
138
139
  - [Video](https://yet-another-react-lightbox.com/plugins/video) - adds support for video slides
139
140
  - [Zoom](https://yet-another-react-lightbox.com/plugins/zoom) - adds image zoom feature
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as React from 'react';
2
2
  import { LightboxExternalProps, LightboxProps, Labels, Slide, SlideImage, LengthOrPercentage, ContainerRect, ToolbarSettings, Component, Module, Node, Plugin, Augmentation, EventTypes, ComponentProps, LightboxStateSwipeAction, LightboxStateUpdateAction, LightboxState, Render, ImageFit, ControllerRef, Callback, RenderFunction } from './types.js';
3
- export { ACTION_CLOSE, ACTION_NEXT, ACTION_PREV, ACTION_SWIPE, ACTIVE_SLIDE_COMPLETE, ACTIVE_SLIDE_ERROR, ACTIVE_SLIDE_LOADING, ACTIVE_SLIDE_PLAYING, AnimationSettings, CLASS_FLEX_CENTER, CLASS_FULLSIZE, CLASS_NO_SCROLL, CLASS_NO_SCROLL_PADDING, Callbacks, CarouselSettings, ClickCallbackProps, ControllerSettings, DeepNonNullable, DeepPartial, DeepPartialValue, ELEMENT_BUTTON, ELEMENT_ICON, EVENT_ON_KEY_DOWN, EVENT_ON_KEY_UP, EVENT_ON_POINTER_CANCEL, EVENT_ON_POINTER_DOWN, EVENT_ON_POINTER_LEAVE, EVENT_ON_POINTER_MOVE, EVENT_ON_POINTER_UP, EVENT_ON_WHEEL, GenericSlide, IMAGE_FIT_CONTAIN, IMAGE_FIT_COVER, ImageSource, MODULE_CAROUSEL, MODULE_CONTROLLER, MODULE_NAVIGATION, MODULE_NO_SCROLL, MODULE_PORTAL, MODULE_ROOT, MODULE_TOOLBAR, NavigationAction, PLUGIN_CAPTIONS, PLUGIN_COUNTER, PLUGIN_DOWNLOAD, PLUGIN_FULLSCREEN, PLUGIN_INLINE, PLUGIN_SLIDESHOW, PLUGIN_THUMBNAILS, PLUGIN_ZOOM, PluginProps, PortalSettings, RenderSlideContainerProps, RenderSlideFooterProps, RenderSlideHeaderProps, RenderSlideProps, SLIDE_STATUS_COMPLETE, SLIDE_STATUS_ERROR, SLIDE_STATUS_LOADING, SLIDE_STATUS_PLACEHOLDER, SLIDE_STATUS_PLAYING, SlideStatus, SlideTypeKey, SlideTypes, Slot, SlotStyles, SlotType, ToolbarButtonKey, ToolbarButtonKeys, UNKNOWN_ACTION_TYPE, VK_ARROW_LEFT, VK_ARROW_RIGHT, VK_ESCAPE, ViewCallbackProps, activeSlideStatus } from './types.js';
3
+ export { ACTION_CLOSE, ACTION_NEXT, ACTION_PREV, ACTION_SWIPE, ACTIVE_SLIDE_COMPLETE, ACTIVE_SLIDE_ERROR, ACTIVE_SLIDE_LOADING, ACTIVE_SLIDE_PLAYING, AnimationSettings, CLASS_FLEX_CENTER, CLASS_FULLSIZE, CLASS_NO_SCROLL, CLASS_NO_SCROLL_PADDING, Callbacks, CarouselSettings, ClickCallbackProps, ControllerSettings, DeepNonNullable, DeepPartial, DeepPartialValue, ELEMENT_BUTTON, ELEMENT_ICON, EVENT_ON_KEY_DOWN, EVENT_ON_KEY_UP, EVENT_ON_POINTER_CANCEL, EVENT_ON_POINTER_DOWN, EVENT_ON_POINTER_LEAVE, EVENT_ON_POINTER_MOVE, EVENT_ON_POINTER_UP, EVENT_ON_WHEEL, GenericSlide, IMAGE_FIT_CONTAIN, IMAGE_FIT_COVER, ImageSource, MODULE_CAROUSEL, MODULE_CONTROLLER, MODULE_NAVIGATION, MODULE_NO_SCROLL, MODULE_PORTAL, MODULE_ROOT, MODULE_TOOLBAR, NavigationAction, PLUGIN_CAPTIONS, PLUGIN_COUNTER, PLUGIN_DOWNLOAD, PLUGIN_FULLSCREEN, PLUGIN_INLINE, PLUGIN_SHARE, PLUGIN_SLIDESHOW, PLUGIN_THUMBNAILS, PLUGIN_ZOOM, PluginProps, PortalSettings, RenderSlideContainerProps, RenderSlideFooterProps, RenderSlideHeaderProps, RenderSlideProps, SLIDE_STATUS_COMPLETE, SLIDE_STATUS_ERROR, SLIDE_STATUS_LOADING, SLIDE_STATUS_PLACEHOLDER, SLIDE_STATUS_PLAYING, SlideStatus, SlideTypeKey, SlideTypes, Slot, SlotStyles, SlotType, ToolbarButtonKey, ToolbarButtonKeys, UNKNOWN_ACTION_TYPE, VK_ARROW_LEFT, VK_ARROW_RIGHT, VK_ESCAPE, ViewCallbackProps, activeSlideStatus } from './types.js';
4
4
 
5
5
  /** Lightbox component */
6
6
  declare function Lightbox({ carousel, animation, render, toolbar, controller, on, plugins, slides, index, ...restProps }: LightboxExternalProps): React.JSX.Element | null;
@@ -79,7 +79,7 @@ declare function useForkRef<InstanceA, InstanceB>(refA: React.Ref<InstanceA> | n
79
79
 
80
80
  declare const useLayoutEffect: typeof React.useEffect;
81
81
 
82
- declare function useLoseFocus(disabled?: boolean): {
82
+ declare function useLoseFocus(focus: () => void, disabled?: boolean): {
83
83
  onFocus: () => void;
84
84
  onBlur: () => void;
85
85
  };
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  'use client';
2
2
  import * as React from 'react';
3
- import { ACTION_CLOSE, IMAGE_FIT_CONTAIN, MODULE_CONTROLLER, IMAGE_FIT_COVER, UNKNOWN_ACTION_TYPE, ELEMENT_BUTTON, ELEMENT_ICON, EVENT_ON_POINTER_DOWN, EVENT_ON_POINTER_MOVE, EVENT_ON_POINTER_UP, EVENT_ON_POINTER_LEAVE, EVENT_ON_POINTER_CANCEL, EVENT_ON_WHEEL, ACTION_PREV, ACTION_NEXT, ACTION_SWIPE, EVENT_ON_KEY_UP, VK_ESCAPE, CLASS_FLEX_CENTER, EVENT_ON_KEY_DOWN, SLIDE_STATUS_LOADING, activeSlideStatus, SLIDE_STATUS_COMPLETE, SLIDE_STATUS_ERROR, SLIDE_STATUS_PLACEHOLDER, MODULE_CAROUSEL, CLASS_FULLSIZE, VK_ARROW_LEFT, VK_ARROW_RIGHT, MODULE_NAVIGATION, CLASS_NO_SCROLL, CLASS_NO_SCROLL_PADDING, MODULE_NO_SCROLL, MODULE_PORTAL, MODULE_ROOT, MODULE_TOOLBAR } from './types.js';
3
+ import { ACTION_CLOSE, IMAGE_FIT_CONTAIN, MODULE_CONTROLLER, IMAGE_FIT_COVER, UNKNOWN_ACTION_TYPE, ELEMENT_BUTTON, ELEMENT_ICON, EVENT_ON_POINTER_DOWN, EVENT_ON_POINTER_MOVE, EVENT_ON_POINTER_UP, EVENT_ON_POINTER_LEAVE, EVENT_ON_POINTER_CANCEL, EVENT_ON_KEY_DOWN, EVENT_ON_KEY_UP, EVENT_ON_WHEEL, SLIDE_STATUS_LOADING, activeSlideStatus, SLIDE_STATUS_COMPLETE, SLIDE_STATUS_ERROR, SLIDE_STATUS_PLACEHOLDER, ACTION_PREV, ACTION_NEXT, ACTION_SWIPE, VK_ESCAPE, CLASS_FLEX_CENTER, MODULE_CAROUSEL, CLASS_FULLSIZE, VK_ARROW_LEFT, VK_ARROW_RIGHT, MODULE_NAVIGATION, CLASS_NO_SCROLL, CLASS_NO_SCROLL_PADDING, MODULE_NO_SCROLL, MODULE_PORTAL, MODULE_ROOT, MODULE_TOOLBAR } from './types.js';
4
4
  import { createPortal } from 'react-dom';
5
- export { ACTIVE_SLIDE_COMPLETE, ACTIVE_SLIDE_ERROR, ACTIVE_SLIDE_LOADING, ACTIVE_SLIDE_PLAYING, PLUGIN_CAPTIONS, PLUGIN_COUNTER, PLUGIN_DOWNLOAD, PLUGIN_FULLSCREEN, PLUGIN_INLINE, PLUGIN_SLIDESHOW, PLUGIN_THUMBNAILS, PLUGIN_ZOOM, SLIDE_STATUS_PLAYING } from './types.js';
5
+ export { ACTIVE_SLIDE_COMPLETE, ACTIVE_SLIDE_ERROR, ACTIVE_SLIDE_LOADING, ACTIVE_SLIDE_PLAYING, PLUGIN_CAPTIONS, PLUGIN_COUNTER, PLUGIN_DOWNLOAD, PLUGIN_FULLSCREEN, PLUGIN_INLINE, PLUGIN_SHARE, PLUGIN_SLIDESHOW, PLUGIN_THUMBNAILS, PLUGIN_ZOOM, SLIDE_STATUS_PLAYING } from './types.js';
6
6
 
7
7
  const LightboxDefaultProps = {
8
8
  open: false,
@@ -34,6 +34,7 @@ const LightboxDefaultProps = {
34
34
  focus: true,
35
35
  aria: false,
36
36
  touchAction: "none",
37
+ closeOnPullDown: false,
37
38
  closeOnBackdropClick: false,
38
39
  },
39
40
  portal: {},
@@ -488,14 +489,176 @@ function useForkRef(refA, refB) {
488
489
  }, [refA, refB]);
489
490
  }
490
491
 
491
- function usePointerSwipe(subscribeSensors, isSwipeValid, containerWidth, swipeAnimationDuration, onSwipeStart, onSwipeProgress, onSwipeFinish, onSwipeCancel) {
492
+ function useLoseFocus(focus, disabled = false) {
493
+ const focused = React.useRef(disabled);
494
+ useLayoutEffect(() => {
495
+ if (disabled) {
496
+ focus();
497
+ }
498
+ }, [disabled, focus]);
499
+ const onFocus = React.useCallback(() => {
500
+ focused.current = true;
501
+ }, []);
502
+ const onBlur = React.useCallback(() => {
503
+ focused.current = false;
504
+ }, []);
505
+ return { onFocus, onBlur };
506
+ }
507
+
508
+ function useRTL() {
509
+ const [isRTL, setIsRTL] = React.useState(false);
510
+ useLayoutEffect(() => {
511
+ setIsRTL(window.getComputedStyle(window.document.documentElement).direction === "rtl");
512
+ }, []);
513
+ return isRTL;
514
+ }
515
+
516
+ function useSensors() {
517
+ const [subscribers] = React.useState({});
518
+ return React.useMemo(() => {
519
+ const notifySubscribers = (type, event) => {
520
+ var _a;
521
+ (_a = subscribers[type]) === null || _a === void 0 ? void 0 : _a.forEach((listener) => {
522
+ if (!event.isPropagationStopped())
523
+ listener(event);
524
+ });
525
+ };
526
+ return {
527
+ registerSensors: {
528
+ onPointerDown: (event) => notifySubscribers(EVENT_ON_POINTER_DOWN, event),
529
+ onPointerMove: (event) => notifySubscribers(EVENT_ON_POINTER_MOVE, event),
530
+ onPointerUp: (event) => notifySubscribers(EVENT_ON_POINTER_UP, event),
531
+ onPointerLeave: (event) => notifySubscribers(EVENT_ON_POINTER_LEAVE, event),
532
+ onPointerCancel: (event) => notifySubscribers(EVENT_ON_POINTER_CANCEL, event),
533
+ onKeyDown: (event) => notifySubscribers(EVENT_ON_KEY_DOWN, event),
534
+ onKeyUp: (event) => notifySubscribers(EVENT_ON_KEY_UP, event),
535
+ onWheel: (event) => notifySubscribers(EVENT_ON_WHEEL, event),
536
+ },
537
+ subscribeSensors: (type, callback) => {
538
+ if (!subscribers[type]) {
539
+ subscribers[type] = [];
540
+ }
541
+ subscribers[type].unshift(callback);
542
+ return () => {
543
+ const listeners = subscribers[type];
544
+ if (listeners) {
545
+ listeners.splice(0, listeners.length, ...listeners.filter((el) => el !== callback));
546
+ }
547
+ };
548
+ },
549
+ };
550
+ }, [subscribers]);
551
+ }
552
+
553
+ function useThrottle(callback, delay) {
554
+ const lastCallbackTime = React.useRef(0);
555
+ const delayCallback = useDelay();
556
+ const executeCallback = useEventCallback((...args) => {
557
+ lastCallbackTime.current = Date.now();
558
+ callback(args);
559
+ });
560
+ return React.useCallback((...args) => {
561
+ delayCallback(() => {
562
+ executeCallback(args);
563
+ }, delay - (Date.now() - lastCallbackTime.current));
564
+ }, [delay, executeCallback, delayCallback]);
565
+ }
566
+
567
+ const slidePrefix = makeComposePrefix("slide");
568
+ const slideImagePrefix = makeComposePrefix("slide_image");
569
+ function ImageSlide({ slide: image, offset, render, rect, imageFit, onClick, onLoad, style }) {
570
+ var _a, _b, _c, _d, _e, _f, _g;
571
+ const [status, setStatus] = React.useState(SLIDE_STATUS_LOADING);
572
+ const { publish } = useEvents();
573
+ const { setTimeout } = useTimeouts();
574
+ const imageRef = React.useRef(null);
575
+ React.useEffect(() => {
576
+ if (offset === 0) {
577
+ publish(activeSlideStatus(status));
578
+ }
579
+ }, [offset, status, publish]);
580
+ const handleLoading = useEventCallback((img) => {
581
+ ("decode" in img ? img.decode() : Promise.resolve())
582
+ .catch(() => { })
583
+ .then(() => {
584
+ if (!img.parentNode) {
585
+ return;
586
+ }
587
+ setStatus(SLIDE_STATUS_COMPLETE);
588
+ setTimeout(() => {
589
+ onLoad === null || onLoad === void 0 ? void 0 : onLoad(img);
590
+ }, 0);
591
+ });
592
+ });
593
+ const setImageRef = React.useCallback((img) => {
594
+ imageRef.current = img;
595
+ if (img === null || img === void 0 ? void 0 : img.complete) {
596
+ handleLoading(img);
597
+ }
598
+ }, [handleLoading]);
599
+ const handleOnLoad = React.useCallback((event) => {
600
+ handleLoading(event.currentTarget);
601
+ }, [handleLoading]);
602
+ const onError = React.useCallback(() => {
603
+ setStatus(SLIDE_STATUS_ERROR);
604
+ }, []);
605
+ const cover = isImageFitCover(image, imageFit);
606
+ const nonInfinite = (value, fallback) => (Number.isFinite(value) ? value : fallback);
607
+ const maxWidth = nonInfinite(Math.max(...((_b = (_a = image.srcSet) === null || _a === void 0 ? void 0 : _a.map((x) => x.width)) !== null && _b !== void 0 ? _b : []).concat(image.width ? [image.width] : [])), ((_c = imageRef.current) === null || _c === void 0 ? void 0 : _c.naturalWidth) || 0);
608
+ const maxHeight = nonInfinite(Math.max(...((_e = (_d = image.srcSet) === null || _d === void 0 ? void 0 : _d.map((x) => x.height)) !== null && _e !== void 0 ? _e : []).concat(image.height ? [image.height] : [])), ((_f = imageRef.current) === null || _f === void 0 ? void 0 : _f.naturalHeight) || 0);
609
+ const defaultStyle = maxWidth && maxHeight
610
+ ? {
611
+ maxWidth: `min(${maxWidth}px, 100%)`,
612
+ maxHeight: `min(${maxHeight}px, 100%)`,
613
+ }
614
+ : {
615
+ maxWidth: "100%",
616
+ maxHeight: "100%",
617
+ };
618
+ const srcSet = (_g = image.srcSet) === null || _g === void 0 ? void 0 : _g.sort((a, b) => a.width - b.width).map((item) => `${item.src} ${item.width}w`).join(", ");
619
+ const estimateActualWidth = () => rect && !cover && image.width && image.height ? (rect.height / image.height) * image.width : Number.MAX_VALUE;
620
+ const sizes = srcSet && rect && hasWindow() ? `${Math.round(Math.min(estimateActualWidth(), rect.width))}px` : undefined;
621
+ return (React.createElement(React.Fragment, null,
622
+ React.createElement("img", { ref: setImageRef, onLoad: handleOnLoad, onError: onError, onClick: onClick, className: clsx(cssClass(slideImagePrefix()), cover && cssClass(slideImagePrefix("cover")), status !== SLIDE_STATUS_COMPLETE && cssClass(slideImagePrefix("loading"))), draggable: false, alt: image.alt, style: { ...defaultStyle, ...style }, sizes: sizes, srcSet: srcSet, src: image.src }),
623
+ status !== SLIDE_STATUS_COMPLETE && (React.createElement("div", { className: cssClass(slidePrefix(SLIDE_STATUS_PLACEHOLDER)) },
624
+ status === SLIDE_STATUS_LOADING &&
625
+ ((render === null || render === void 0 ? void 0 : render.iconLoading) ? (render.iconLoading()) : (React.createElement(LoadingIcon, { className: clsx(cssClass(ELEMENT_ICON), cssClass(slidePrefix(SLIDE_STATUS_LOADING))) }))),
626
+ status === SLIDE_STATUS_ERROR &&
627
+ ((render === null || render === void 0 ? void 0 : render.iconError) ? (render.iconError()) : (React.createElement(ErrorIcon, { className: clsx(cssClass(ELEMENT_ICON), cssClass(slidePrefix(SLIDE_STATUS_ERROR))) })))))));
628
+ }
629
+
630
+ var SwipeState;
631
+ (function (SwipeState) {
632
+ SwipeState[SwipeState["NONE"] = 0] = "NONE";
633
+ SwipeState[SwipeState["SWIPE"] = 1] = "SWIPE";
634
+ SwipeState[SwipeState["ANIMATION"] = 2] = "ANIMATION";
635
+ })(SwipeState || (SwipeState = {}));
636
+
637
+ function usePointerEvents(subscribeSensors, onPointerDown, onPointerMove, onPointerUp, disabled) {
638
+ React.useEffect(() => {
639
+ if (disabled)
640
+ return () => { };
641
+ return cleanup(subscribeSensors(EVENT_ON_POINTER_DOWN, onPointerDown), subscribeSensors(EVENT_ON_POINTER_MOVE, onPointerMove), subscribeSensors(EVENT_ON_POINTER_UP, onPointerUp), subscribeSensors(EVENT_ON_POINTER_LEAVE, onPointerUp), subscribeSensors(EVENT_ON_POINTER_CANCEL, onPointerUp));
642
+ }, [subscribeSensors, onPointerDown, onPointerMove, onPointerUp, disabled]);
643
+ }
644
+
645
+ var Gesture;
646
+ (function (Gesture) {
647
+ Gesture[Gesture["NONE"] = 0] = "NONE";
648
+ Gesture[Gesture["SWIPE"] = 1] = "SWIPE";
649
+ Gesture[Gesture["PULL_DOWN"] = 2] = "PULL_DOWN";
650
+ })(Gesture || (Gesture = {}));
651
+ const SWIPE_THRESHOLD = 30;
652
+ function usePointerSwipe(subscribeSensors, isSwipeValid, containerWidth, swipeAnimationDuration, onSwipeStart, onSwipeProgress, onSwipeFinish, onSwipeCancel, onPullDownStart, onPullDownProgress, onPullDownFinish, onPullDownCancel) {
492
653
  const offset = React.useRef(0);
493
654
  const pointers = React.useRef([]);
494
655
  const activePointer = React.useRef();
495
656
  const startTime = React.useRef(0);
657
+ const gesture = React.useRef(Gesture.NONE);
496
658
  const clearPointer = React.useCallback((event) => {
497
659
  if (activePointer.current === event.pointerId) {
498
660
  activePointer.current = undefined;
661
+ gesture.current = Gesture.NONE;
499
662
  }
500
663
  const currentPointers = pointers.current;
501
664
  currentPointers.splice(0, currentPointers.length, ...currentPointers.filter((p) => p.pointerId !== event.pointerId));
@@ -513,14 +676,25 @@ function usePointerSwipe(subscribeSensors, isSwipeValid, containerWidth, swipeAn
513
676
  activePointer.current === event.pointerId) {
514
677
  const duration = Date.now() - startTime.current;
515
678
  const currentOffset = offset.current;
516
- if (Math.abs(currentOffset) > 0.3 * containerWidth ||
517
- (Math.abs(currentOffset) > 5 && duration < swipeAnimationDuration)) {
518
- onSwipeFinish(currentOffset, duration);
679
+ if (gesture.current === Gesture.SWIPE) {
680
+ if (Math.abs(currentOffset) > 0.3 * containerWidth ||
681
+ (Math.abs(currentOffset) > 5 && duration < swipeAnimationDuration)) {
682
+ onSwipeFinish(currentOffset, duration);
683
+ }
684
+ else {
685
+ onSwipeCancel(currentOffset);
686
+ }
519
687
  }
520
- else {
521
- onSwipeCancel(currentOffset);
688
+ else if (gesture.current === Gesture.PULL_DOWN) {
689
+ if (currentOffset > 2 * SWIPE_THRESHOLD) {
690
+ onPullDownFinish(currentOffset, duration);
691
+ }
692
+ else {
693
+ onPullDownCancel(currentOffset);
694
+ }
522
695
  }
523
696
  offset.current = 0;
697
+ gesture.current = Gesture.NONE;
524
698
  }
525
699
  clearPointer(event);
526
700
  });
@@ -539,22 +713,35 @@ function usePointerSwipe(subscribeSensors, isSwipeValid, containerWidth, swipeAn
539
713
  }
540
714
  const deltaX = event.clientX - pointer.clientX;
541
715
  const deltaY = event.clientY - pointer.clientY;
542
- if (activePointer.current === undefined &&
543
- isSwipeValid(deltaX) &&
544
- Math.abs(deltaX) > Math.abs(deltaY) &&
545
- Math.abs(deltaX) > 30) {
546
- addPointer(event);
547
- activePointer.current = event.pointerId;
548
- startTime.current = Date.now();
549
- onSwipeStart();
716
+ if (activePointer.current === undefined) {
717
+ const startGesture = (newGesture) => {
718
+ addPointer(event);
719
+ activePointer.current = event.pointerId;
720
+ startTime.current = Date.now();
721
+ gesture.current = newGesture;
722
+ };
723
+ if (Math.abs(deltaX) > Math.abs(deltaY) && Math.abs(deltaX) > SWIPE_THRESHOLD && isSwipeValid(deltaX)) {
724
+ startGesture(Gesture.SWIPE);
725
+ onSwipeStart();
726
+ }
727
+ else if (Math.abs(deltaY) > Math.abs(deltaX) && Math.abs(deltaY) > SWIPE_THRESHOLD) {
728
+ startGesture(Gesture.PULL_DOWN);
729
+ onPullDownStart();
730
+ }
550
731
  }
551
732
  else if (isCurrentPointer) {
552
- offset.current = deltaX;
553
- onSwipeProgress(deltaX);
733
+ if (gesture.current === Gesture.SWIPE) {
734
+ offset.current = deltaX;
735
+ onSwipeProgress(deltaX);
736
+ }
737
+ else if (gesture.current === Gesture.PULL_DOWN) {
738
+ offset.current = deltaY;
739
+ onPullDownProgress(deltaY);
740
+ }
554
741
  }
555
742
  }
556
743
  });
557
- React.useEffect(() => cleanup(subscribeSensors(EVENT_ON_POINTER_DOWN, onPointerDown), subscribeSensors(EVENT_ON_POINTER_MOVE, onPointerMove), subscribeSensors(EVENT_ON_POINTER_UP, onPointerUp), subscribeSensors(EVENT_ON_POINTER_LEAVE, onPointerUp), subscribeSensors(EVENT_ON_POINTER_CANCEL, onPointerUp)), [subscribeSensors, onPointerDown, onPointerMove, onPointerUp]);
744
+ usePointerEvents(subscribeSensors, onPointerDown, onPointerMove, onPointerUp);
558
745
  }
559
746
 
560
747
  const WHEEL = "wheel";
@@ -665,13 +852,6 @@ function useWheelSwipe(swipeState, subscribeSensors, isSwipeValid, containerWidt
665
852
  React.useEffect(() => subscribeSensors(EVENT_ON_WHEEL, onWheel), [subscribeSensors, onWheel]);
666
853
  }
667
854
 
668
- var SwipeState;
669
- (function (SwipeState) {
670
- SwipeState[SwipeState["NONE"] = 0] = "NONE";
671
- SwipeState[SwipeState["SWIPE"] = 1] = "SWIPE";
672
- SwipeState[SwipeState["ANIMATION"] = 2] = "ANIMATION";
673
- })(SwipeState || (SwipeState = {}));
674
-
675
855
  const cssContainerPrefix = makeComposePrefix("container");
676
856
  const ControllerContext = React.createContext(null);
677
857
  const useController = makeUseContext("useController", "ControllerContext", ControllerContext);
@@ -814,7 +994,17 @@ function Controller({ children, ...props }) {
814
994
  (offset, duration) => swipe({ offset, duration, count: 1 }),
815
995
  (offset) => swipe({ offset, count: 0 }),
816
996
  ];
817
- usePointerSwipe(...swipeParams);
997
+ const pullDownParams = [
998
+ () => { },
999
+ () => { },
1000
+ () => {
1001
+ if (controller.closeOnPullDown) {
1002
+ close();
1003
+ }
1004
+ },
1005
+ () => { },
1006
+ ];
1007
+ usePointerSwipe(...swipeParams, ...pullDownParams);
818
1008
  useWheelSwipe(swipeState, ...swipeParams);
819
1009
  const focusOnMount = useEventCallback(() => {
820
1010
  var _a;
@@ -881,145 +1071,6 @@ function Controller({ children, ...props }) {
881
1071
  }
882
1072
  const ControllerModule = createModule(MODULE_CONTROLLER, Controller);
883
1073
 
884
- function useLoseFocus(disabled = false) {
885
- const focused = React.useRef(disabled);
886
- const { focus } = useController();
887
- useLayoutEffect(() => {
888
- if (disabled) {
889
- focus();
890
- }
891
- }, [disabled, focus]);
892
- const onFocus = React.useCallback(() => {
893
- focused.current = true;
894
- }, []);
895
- const onBlur = React.useCallback(() => {
896
- focused.current = false;
897
- }, []);
898
- return { onFocus, onBlur };
899
- }
900
-
901
- function useRTL() {
902
- const [isRTL, setIsRTL] = React.useState(false);
903
- useLayoutEffect(() => {
904
- setIsRTL(window.getComputedStyle(window.document.documentElement).direction === "rtl");
905
- }, []);
906
- return isRTL;
907
- }
908
-
909
- function useSensors() {
910
- const [subscribers] = React.useState({});
911
- return React.useMemo(() => {
912
- const notifySubscribers = (type, event) => {
913
- var _a;
914
- (_a = subscribers[type]) === null || _a === void 0 ? void 0 : _a.forEach((listener) => {
915
- if (!event.isPropagationStopped())
916
- listener(event);
917
- });
918
- };
919
- return {
920
- registerSensors: {
921
- onPointerDown: (event) => notifySubscribers(EVENT_ON_POINTER_DOWN, event),
922
- onPointerMove: (event) => notifySubscribers(EVENT_ON_POINTER_MOVE, event),
923
- onPointerUp: (event) => notifySubscribers(EVENT_ON_POINTER_UP, event),
924
- onPointerLeave: (event) => notifySubscribers(EVENT_ON_POINTER_LEAVE, event),
925
- onPointerCancel: (event) => notifySubscribers(EVENT_ON_POINTER_CANCEL, event),
926
- onKeyDown: (event) => notifySubscribers(EVENT_ON_KEY_DOWN, event),
927
- onKeyUp: (event) => notifySubscribers(EVENT_ON_KEY_UP, event),
928
- onWheel: (event) => notifySubscribers(EVENT_ON_WHEEL, event),
929
- },
930
- subscribeSensors: (type, callback) => {
931
- if (!subscribers[type]) {
932
- subscribers[type] = [];
933
- }
934
- subscribers[type].unshift(callback);
935
- return () => {
936
- const listeners = subscribers[type];
937
- if (listeners) {
938
- listeners.splice(0, listeners.length, ...listeners.filter((el) => el !== callback));
939
- }
940
- };
941
- },
942
- };
943
- }, [subscribers]);
944
- }
945
-
946
- function useThrottle(callback, delay) {
947
- const lastCallbackTime = React.useRef(0);
948
- const delayCallback = useDelay();
949
- const executeCallback = useEventCallback((...args) => {
950
- lastCallbackTime.current = Date.now();
951
- callback(args);
952
- });
953
- return React.useCallback((...args) => {
954
- delayCallback(() => {
955
- executeCallback(args);
956
- }, delay - (Date.now() - lastCallbackTime.current));
957
- }, [delay, executeCallback, delayCallback]);
958
- }
959
-
960
- const slidePrefix = makeComposePrefix("slide");
961
- const slideImagePrefix = makeComposePrefix("slide_image");
962
- function ImageSlide({ slide: image, offset, render, rect, imageFit, onClick, onLoad, style }) {
963
- var _a, _b, _c, _d, _e, _f, _g;
964
- const [status, setStatus] = React.useState(SLIDE_STATUS_LOADING);
965
- const { publish } = useEvents();
966
- const { setTimeout } = useTimeouts();
967
- const imageRef = React.useRef(null);
968
- React.useEffect(() => {
969
- if (offset === 0) {
970
- publish(activeSlideStatus(status));
971
- }
972
- }, [offset, status, publish]);
973
- const handleLoading = useEventCallback((img) => {
974
- ("decode" in img ? img.decode() : Promise.resolve())
975
- .catch(() => { })
976
- .then(() => {
977
- if (!img.parentNode) {
978
- return;
979
- }
980
- setStatus(SLIDE_STATUS_COMPLETE);
981
- setTimeout(() => {
982
- onLoad === null || onLoad === void 0 ? void 0 : onLoad(img);
983
- }, 0);
984
- });
985
- });
986
- const setImageRef = React.useCallback((img) => {
987
- imageRef.current = img;
988
- if (img === null || img === void 0 ? void 0 : img.complete) {
989
- handleLoading(img);
990
- }
991
- }, [handleLoading]);
992
- const handleOnLoad = React.useCallback((event) => {
993
- handleLoading(event.currentTarget);
994
- }, [handleLoading]);
995
- const onError = React.useCallback(() => {
996
- setStatus(SLIDE_STATUS_ERROR);
997
- }, []);
998
- const cover = isImageFitCover(image, imageFit);
999
- const nonInfinite = (value, fallback) => (Number.isFinite(value) ? value : fallback);
1000
- const maxWidth = nonInfinite(Math.max(...((_b = (_a = image.srcSet) === null || _a === void 0 ? void 0 : _a.map((x) => x.width)) !== null && _b !== void 0 ? _b : []).concat(image.width ? [image.width] : [])), ((_c = imageRef.current) === null || _c === void 0 ? void 0 : _c.naturalWidth) || 0);
1001
- const maxHeight = nonInfinite(Math.max(...((_e = (_d = image.srcSet) === null || _d === void 0 ? void 0 : _d.map((x) => x.height)) !== null && _e !== void 0 ? _e : []).concat(image.height ? [image.height] : [])), ((_f = imageRef.current) === null || _f === void 0 ? void 0 : _f.naturalHeight) || 0);
1002
- const defaultStyle = maxWidth && maxHeight
1003
- ? {
1004
- maxWidth: `min(${maxWidth}px, 100%)`,
1005
- maxHeight: `min(${maxHeight}px, 100%)`,
1006
- }
1007
- : {
1008
- maxWidth: "100%",
1009
- maxHeight: "100%",
1010
- };
1011
- const srcSet = (_g = image.srcSet) === null || _g === void 0 ? void 0 : _g.sort((a, b) => a.width - b.width).map((item) => `${item.src} ${item.width}w`).join(", ");
1012
- const estimateActualWidth = () => rect && !cover && image.width && image.height ? (rect.height / image.height) * image.width : Number.MAX_VALUE;
1013
- const sizes = srcSet && rect && hasWindow() ? `${Math.round(Math.min(estimateActualWidth(), rect.width))}px` : undefined;
1014
- return (React.createElement(React.Fragment, null,
1015
- React.createElement("img", { ref: setImageRef, onLoad: handleOnLoad, onError: onError, onClick: onClick, className: clsx(cssClass(slideImagePrefix()), cover && cssClass(slideImagePrefix("cover")), status !== SLIDE_STATUS_COMPLETE && cssClass(slideImagePrefix("loading"))), draggable: false, alt: image.alt, style: { ...defaultStyle, ...style }, sizes: sizes, srcSet: srcSet, src: image.src }),
1016
- status !== SLIDE_STATUS_COMPLETE && (React.createElement("div", { className: cssClass(slidePrefix(SLIDE_STATUS_PLACEHOLDER)) },
1017
- status === SLIDE_STATUS_LOADING &&
1018
- ((render === null || render === void 0 ? void 0 : render.iconLoading) ? (render.iconLoading()) : (React.createElement(LoadingIcon, { className: clsx(cssClass(ELEMENT_ICON), cssClass(slidePrefix(SLIDE_STATUS_LOADING))) }))),
1019
- status === SLIDE_STATUS_ERROR &&
1020
- ((render === null || render === void 0 ? void 0 : render.iconError) ? (render.iconError()) : (React.createElement(ErrorIcon, { className: clsx(cssClass(ELEMENT_ICON), cssClass(slidePrefix(SLIDE_STATUS_ERROR))) })))))));
1021
- }
1022
-
1023
1074
  function cssPrefix$2(value) {
1024
1075
  return composePrefix(MODULE_CAROUSEL, value);
1025
1076
  }
@@ -1087,7 +1138,7 @@ function Carousel({ carousel: { finite, preload, padding, spacing } }) {
1087
1138
  const CarouselModule = createModule(MODULE_CAROUSEL, Carousel);
1088
1139
 
1089
1140
  function NavigationButton({ label, icon, renderIcon, action, onClick, disabled }) {
1090
- return (React.createElement(IconButton, { label: label, icon: icon, renderIcon: renderIcon, className: cssClass(`navigation_${action}`), disabled: disabled, onClick: onClick, ...useLoseFocus(disabled) }));
1141
+ return (React.createElement(IconButton, { label: label, icon: icon, renderIcon: renderIcon, className: cssClass(`navigation_${action}`), disabled: disabled, onClick: onClick, ...useLoseFocus(useController().focus, disabled) }));
1091
1142
  }
1092
1143
  function Navigation({ carousel: { finite }, animation, render: { buttonPrev, buttonNext, iconPrev, iconNext }, }) {
1093
1144
  var _a;
@@ -1314,4 +1365,4 @@ function Lightbox({ carousel, animation, render, toolbar, controller, on, plugin
1314
1365
  React.createElement(EventsProvider, null, renderNode(createNode(RootModule, config), props))))));
1315
1366
  }
1316
1367
 
1317
- export { ACTION_CLOSE, ACTION_NEXT, ACTION_PREV, ACTION_SWIPE, CLASS_FLEX_CENTER, CLASS_FULLSIZE, CLASS_NO_SCROLL, CLASS_NO_SCROLL_PADDING, Carousel, CarouselModule, CloseIcon, Controller, ControllerContext, ControllerModule, ELEMENT_BUTTON, ELEMENT_ICON, EVENT_ON_KEY_DOWN, EVENT_ON_KEY_UP, EVENT_ON_POINTER_CANCEL, EVENT_ON_POINTER_DOWN, EVENT_ON_POINTER_LEAVE, EVENT_ON_POINTER_MOVE, EVENT_ON_POINTER_UP, EVENT_ON_WHEEL, ErrorIcon, EventsContext, EventsProvider, IMAGE_FIT_CONTAIN, IMAGE_FIT_COVER, IconButton, ImageSlide, Lightbox, LightboxDefaultProps, LightboxDispatchContext, LightboxPropsContext, LightboxPropsProvider, LightboxStateContext, LightboxStateProvider, LoadingIcon, MODULE_CAROUSEL, MODULE_CONTROLLER, MODULE_NAVIGATION, MODULE_NO_SCROLL, MODULE_PORTAL, MODULE_ROOT, MODULE_TOOLBAR, Navigation, NavigationButton, NavigationModule, NextIcon, NoScroll, NoScrollModule, Portal, PortalModule, PreviousIcon, Root, RootModule, SLIDE_STATUS_COMPLETE, SLIDE_STATUS_ERROR, SLIDE_STATUS_LOADING, SLIDE_STATUS_PLACEHOLDER, TimeoutsContext, TimeoutsProvider, Toolbar, ToolbarModule, UNKNOWN_ACTION_TYPE, VK_ARROW_LEFT, VK_ARROW_RIGHT, VK_ESCAPE, activeSlideStatus, addToolbarButton, cleanup, clsx, composePrefix, computeSlideRect, createIcon, createIconDisabled, createModule, createNode, cssClass, cssVar, Lightbox as default, devicePixelRatio, getSlide, getSlideIfPresent, getSlideIndex, hasSlides, hasWindow, isImageFitCover, isImageSlide, label, makeComposePrefix, makeUseContext, parseLengthPercentage, round, setRef, stopNavigationEventsPropagation, useAnimation, useContainerRect, useController, useDelay, useEventCallback, useEvents, useForkRef, useLayoutEffect, useLightboxDispatch, useLightboxProps, useLightboxState, useLoseFocus, useMotionPreference, useRTL, useSensors, useThrottle, useTimeouts, withPlugins };
1368
+ export { ACTION_CLOSE, ACTION_NEXT, ACTION_PREV, ACTION_SWIPE, CLASS_FLEX_CENTER, CLASS_FULLSIZE, CLASS_NO_SCROLL, CLASS_NO_SCROLL_PADDING, Carousel, CarouselModule, CloseIcon, Controller, ControllerContext, ControllerModule, ELEMENT_BUTTON, ELEMENT_ICON, EVENT_ON_KEY_DOWN, EVENT_ON_KEY_UP, EVENT_ON_POINTER_CANCEL, EVENT_ON_POINTER_DOWN, EVENT_ON_POINTER_LEAVE, EVENT_ON_POINTER_MOVE, EVENT_ON_POINTER_UP, EVENT_ON_WHEEL, ErrorIcon, EventsContext, EventsProvider, IMAGE_FIT_CONTAIN, IMAGE_FIT_COVER, IconButton, ImageSlide, Lightbox, LightboxDefaultProps, LightboxDispatchContext, LightboxPropsContext, LightboxPropsProvider, LightboxStateContext, LightboxStateProvider, LoadingIcon, MODULE_CAROUSEL, MODULE_CONTROLLER, MODULE_NAVIGATION, MODULE_NO_SCROLL, MODULE_PORTAL, MODULE_ROOT, MODULE_TOOLBAR, Navigation, NavigationButton, NavigationModule, NextIcon, NoScroll, NoScrollModule, Portal, PortalModule, PreviousIcon, Root, RootModule, SLIDE_STATUS_COMPLETE, SLIDE_STATUS_ERROR, SLIDE_STATUS_LOADING, SLIDE_STATUS_PLACEHOLDER, TimeoutsContext, TimeoutsProvider, Toolbar, ToolbarModule, UNKNOWN_ACTION_TYPE, VK_ARROW_LEFT, VK_ARROW_RIGHT, VK_ESCAPE, activeSlideStatus, addToolbarButton, cleanup, clsx, composePrefix, computeSlideRect, createIcon, createIconDisabled, createModule, createNode, cssClass, cssVar, Lightbox as default, devicePixelRatio, getSlide, getSlideIfPresent, getSlideIndex, hasSlides, hasWindow, isImageFitCover, isImageSlide, label, makeComposePrefix, makeUseContext, parseLengthPercentage, round, setRef, stopNavigationEventsPropagation, useAnimation, useContainerRect, useController, useDelay, useEventCallback, useEvents, useForkRef, useLayoutEffect, useLightboxDispatch, useLightboxProps, useLightboxState, useLoseFocus, useMotionPreference, usePointerEvents, useRTL, useSensors, useThrottle, useTimeouts, withPlugins };
@@ -4,10 +4,24 @@ declare function Download({ augment }: PluginProps): void;
4
4
 
5
5
  declare module "../../types.js" {
6
6
  interface GenericSlide {
7
- /** download url */
7
+ /** @deprecated - use `download` instead */
8
8
  downloadUrl?: string;
9
- /** download filename override */
9
+ /** @deprecated - use `download` instead */
10
10
  downloadFilename?: string;
11
+ /** download url or download props */
12
+ download?: boolean | string | {
13
+ /** download url */
14
+ url: string;
15
+ /** download filename override */
16
+ filename: string;
17
+ };
18
+ }
19
+ interface LightboxProps {
20
+ /** Download plugin settings */
21
+ download?: {
22
+ /** Custom download function */
23
+ download?: ({ slide, saveAs }: DownloadFunctionProps) => void;
24
+ };
11
25
  }
12
26
  interface Render {
13
27
  /** render custom Download button */
@@ -19,11 +33,15 @@ declare module "../../types.js" {
19
33
  /** a callback called on slide download */
20
34
  download?: Callback<DownloadCallbackProps>;
21
35
  }
36
+ interface ToolbarButtonKeys {
37
+ [PLUGIN_DOWNLOAD]: null;
38
+ }
22
39
  interface DownloadCallbackProps {
23
40
  index: number;
24
41
  }
25
- interface ToolbarButtonKeys {
26
- [PLUGIN_DOWNLOAD]: null;
42
+ interface DownloadFunctionProps {
43
+ slide: Slide;
44
+ saveAs: (source: string | Blob, name?: string) => void;
27
45
  }
28
46
  }
29
47
 
@@ -2,6 +2,14 @@ import { createIcon, useLightboxProps, useLightboxState, isImageSlide, IconButto
2
2
  import * as React from 'react';
3
3
  import { PLUGIN_DOWNLOAD } from '../../types.js';
4
4
 
5
+ const defaultDownloadProps = {
6
+ download: undefined,
7
+ };
8
+ const resolveDownloadProps = (download) => ({
9
+ ...defaultDownloadProps,
10
+ ...download,
11
+ });
12
+
5
13
  function download(url, name) {
6
14
  const xhr = new XMLHttpRequest();
7
15
  xhr.open("GET", url);
@@ -65,24 +73,41 @@ function saveAs(source, name) {
65
73
 
66
74
  const DownloadIcon = createIcon("DownloadIcon", React.createElement("path", { d: "M18 15v3H6v-3H4v3c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2v-3h-2zm-1-4-1.41-1.41L13 12.17V4h-2v8.17L8.41 9.59 7 11l5 5 5-5z" }));
67
75
  function DownloadButton() {
68
- const { render, on } = useLightboxProps();
76
+ const { render, on, download: downloadProps } = useLightboxProps();
77
+ const { download: customDownload } = resolveDownloadProps(downloadProps);
69
78
  const { currentSlide, currentIndex } = useLightboxState();
70
79
  if (render.buttonDownload) {
71
80
  return React.createElement(React.Fragment, null, render.buttonDownload());
72
81
  }
73
- const downloadUrl = (currentSlide === null || currentSlide === void 0 ? void 0 : currentSlide.downloadUrl) || (currentSlide && isImageSlide(currentSlide) ? currentSlide.src : undefined);
74
- return (React.createElement(IconButton, { label: "Download", icon: DownloadIcon, renderIcon: render.iconDownload, disabled: !downloadUrl, onClick: () => {
75
- var _a;
76
- if (downloadUrl) {
77
- saveAs(downloadUrl, currentSlide === null || currentSlide === void 0 ? void 0 : currentSlide.downloadFilename);
78
- (_a = on.download) === null || _a === void 0 ? void 0 : _a.call(on, { index: currentIndex });
79
- }
80
- } }));
82
+ const downloadUrl = (currentSlide &&
83
+ (currentSlide.downloadUrl ||
84
+ (typeof currentSlide.download === "string" && currentSlide.download) ||
85
+ (typeof currentSlide.download === "object" && currentSlide.download.url) ||
86
+ (isImageSlide(currentSlide) && currentSlide.src))) ||
87
+ undefined;
88
+ const canDownload = customDownload ? Boolean(currentSlide === null || currentSlide === void 0 ? void 0 : currentSlide.download) : Boolean(downloadUrl);
89
+ const defaultDownload = () => {
90
+ if (currentSlide && downloadUrl) {
91
+ const downloadFilename = currentSlide.downloadFilename ||
92
+ (typeof currentSlide.download === "object" && currentSlide.download.filename) ||
93
+ undefined;
94
+ saveAs(downloadUrl, downloadFilename);
95
+ }
96
+ };
97
+ const handleDownload = () => {
98
+ var _a;
99
+ if (currentSlide) {
100
+ (customDownload || defaultDownload)({ slide: currentSlide, saveAs });
101
+ (_a = on.download) === null || _a === void 0 ? void 0 : _a.call(on, { index: currentIndex });
102
+ }
103
+ };
104
+ return (React.createElement(IconButton, { label: "Download", icon: DownloadIcon, renderIcon: render.iconDownload, disabled: !canDownload, onClick: handleDownload }));
81
105
  }
82
106
 
83
107
  function Download({ augment }) {
84
- augment(({ toolbar, ...restProps }) => ({
108
+ augment(({ toolbar, download, ...restProps }) => ({
85
109
  toolbar: addToolbarButton(toolbar, PLUGIN_DOWNLOAD, React.createElement(DownloadButton, null)),
110
+ download: resolveDownloadProps(download),
86
111
  ...restProps,
87
112
  }));
88
113
  }
@@ -3,6 +3,7 @@ export { default as Counter } from './counter/index.js';
3
3
  export { default as Download } from './download/index.js';
4
4
  export { default as Fullscreen } from './fullscreen/index.js';
5
5
  export { default as Inline } from './inline/index.js';
6
+ export { default as Share } from './share/index.js';
6
7
  export { default as Slideshow } from './slideshow/index.js';
7
8
  export { default as Thumbnails } from './thumbnails/index.js';
8
9
  export { default as Video } from './video/index.js';
@@ -3,6 +3,7 @@ export { default as Counter } from './counter/index.js';
3
3
  export { default as Download } from './download/index.js';
4
4
  export { default as Fullscreen } from './fullscreen/index.js';
5
5
  export { default as Inline } from './inline/index.js';
6
+ export { default as Share } from './share/index.js';
6
7
  export { default as Slideshow } from './slideshow/index.js';
7
8
  export { default as Thumbnails } from './thumbnails/index.js';
8
9
  export { default as Video } from './video/index.js';
@@ -0,0 +1,47 @@
1
+ import { PluginProps, PLUGIN_SHARE } from '../../types.js';
2
+
3
+ declare function Share({ augment }: PluginProps): void;
4
+
5
+ declare function isShareSupported(): boolean;
6
+
7
+ declare module "../../types.js" {
8
+ interface GenericSlide {
9
+ /** share url or share props */
10
+ share?: boolean | string | {
11
+ /** share url */
12
+ url?: string;
13
+ /** share text */
14
+ text?: string;
15
+ /** share title */
16
+ title?: string;
17
+ };
18
+ }
19
+ interface LightboxProps {
20
+ /** Share plugin settings */
21
+ share?: {
22
+ /** custom share function */
23
+ share?: ({ slide }: ShareFunctionProps) => void;
24
+ };
25
+ }
26
+ interface Render {
27
+ /** render custom Share button */
28
+ buttonShare?: RenderFunction;
29
+ /** render custom Share icon */
30
+ iconShare?: RenderFunction;
31
+ }
32
+ interface Callbacks {
33
+ /** a callback called on slide share */
34
+ share?: Callback<ShareCallbackProps>;
35
+ }
36
+ interface ToolbarButtonKeys {
37
+ [PLUGIN_SHARE]: null;
38
+ }
39
+ interface ShareCallbackProps {
40
+ index: number;
41
+ }
42
+ interface ShareFunctionProps {
43
+ slide: Slide;
44
+ }
45
+ }
46
+
47
+ export { Share as default, isShareSupported };
@@ -0,0 +1,55 @@
1
+ import { createIcon, useLightboxProps, useLightboxState, isImageSlide, IconButton, addToolbarButton } from '../../index.js';
2
+ import * as React from 'react';
3
+
4
+ const defaultShareProps = {
5
+ share: undefined,
6
+ };
7
+ const resolveShareProps = (share) => ({
8
+ ...defaultShareProps,
9
+ ...share,
10
+ });
11
+
12
+ function isShareSupported() {
13
+ return Boolean(navigator.canShare);
14
+ }
15
+
16
+ const ShareIcon = createIcon("ShareIcon", React.createElement("path", { d: "m16 5-1.42 1.42-1.59-1.59V16h-1.98V4.83L9.42 6.42 8 5l4-4 4 4zm4 5v11c0 1.1-.9 2-2 2H6c-1.11 0-2-.9-2-2V10c0-1.11.89-2 2-2h3v2H6v11h12V10h-3V8h3c1.1 0 2 .89 2 2z" }));
17
+ function ShareButton() {
18
+ const { render, on, share: shareProps } = useLightboxProps();
19
+ const { share: customShare } = resolveShareProps(shareProps);
20
+ const { currentSlide, currentIndex } = useLightboxState();
21
+ if (!isShareSupported())
22
+ return null;
23
+ if (render.buttonShare) {
24
+ return React.createElement(React.Fragment, null, render.buttonShare());
25
+ }
26
+ const share = (currentSlide &&
27
+ ((typeof currentSlide.share === "object" && currentSlide.share) ||
28
+ (typeof currentSlide.share === "string" && { url: currentSlide.share }) ||
29
+ (isImageSlide(currentSlide) && { url: currentSlide.src }))) ||
30
+ undefined;
31
+ const canShare = customShare ? Boolean(currentSlide === null || currentSlide === void 0 ? void 0 : currentSlide.share) : share && navigator.canShare(share);
32
+ const defaultShare = () => {
33
+ if (share) {
34
+ navigator.share(share).catch(() => { });
35
+ }
36
+ };
37
+ const handleShare = () => {
38
+ var _a;
39
+ if (currentSlide) {
40
+ (customShare || defaultShare)({ slide: currentSlide });
41
+ (_a = on.share) === null || _a === void 0 ? void 0 : _a.call(on, { index: currentIndex });
42
+ }
43
+ };
44
+ return (React.createElement(IconButton, { label: "Share", icon: ShareIcon, renderIcon: render.iconShare, disabled: !canShare, onClick: handleShare }));
45
+ }
46
+
47
+ function Share({ augment }) {
48
+ augment(({ toolbar, share, ...rest }) => ({
49
+ toolbar: addToolbarButton(toolbar, "share", React.createElement(ShareButton, null)),
50
+ share: resolveShareProps(share),
51
+ ...rest,
52
+ }));
53
+ }
54
+
55
+ export { Share as default, isShareSupported };
@@ -94,7 +94,7 @@ const PauseIcon = createIcon("Pause", React.createElement("path", { d: "M6 19h4V
94
94
  function SlideshowButton() {
95
95
  const { playing, disabled, play, pause } = useSlideshow();
96
96
  const { render } = useLightboxProps();
97
- const focusListeners = useLoseFocus(disabled);
97
+ const focusListeners = useLoseFocus(useController().focus, disabled);
98
98
  if (render.buttonSlideshow) {
99
99
  return React.createElement(React.Fragment, null, render.buttonSlideshow({ playing, disabled, play, pause }));
100
100
  }
@@ -80,9 +80,8 @@ function isHorizontal(position) {
80
80
  function boxSize(thumbnails, dimension, includeGap) {
81
81
  return dimension + 2 * (thumbnails.border + thumbnails.padding) + (includeGap ? thumbnails.gap : 0);
82
82
  }
83
- function ThumbnailsTrack({ containerRef }) {
83
+ function ThumbnailsTrack({ visible, containerRef }) {
84
84
  const track = React.useRef(null);
85
- const { visible } = useThumbnails();
86
85
  const { carousel, styles } = useLightboxProps();
87
86
  const { slides, globalIndex, animation } = useLightboxState();
88
87
  const { publish, subscribe } = useEvents();
@@ -222,9 +221,9 @@ function ThumbnailsContextProvider({ children, ...props }) {
222
221
  return (React.createElement(LightboxPropsProvider, { ...props },
223
222
  React.createElement(ThumbnailsContext.Provider, { value: context },
224
223
  React.createElement("div", { ref: containerRef, className: clsx(cssClass(cssPrefix()), cssClass(cssPrefix(`${position}`))) },
225
- ["start", "top"].includes(position) && React.createElement(ThumbnailsTrack, { containerRef: containerRef }),
224
+ ["start", "top"].includes(position) && (React.createElement(ThumbnailsTrack, { containerRef: containerRef, visible: visible })),
226
225
  React.createElement("div", { className: cssClass(cssPrefix("wrapper")) }, children),
227
- ["end", "bottom"].includes(position) && React.createElement(ThumbnailsTrack, { containerRef: containerRef })))));
226
+ ["end", "bottom"].includes(position) && (React.createElement(ThumbnailsTrack, { containerRef: containerRef, visible: visible }))))));
228
227
  }
229
228
 
230
229
  const thumbnailsIcon = () => (React.createElement(React.Fragment, null,
@@ -1,6 +1,6 @@
1
- import { useLightboxProps, useMotionPreference, useEventCallback, useLayoutEffect, useLightboxState, isImageSlide, isImageFitCover, round, useController, cleanup, makeUseContext, createIcon, IconButton, devicePixelRatio, ImageSlide, clsx, cssClass, addToolbarButton, createModule } from '../../index.js';
1
+ import { useLightboxProps, useMotionPreference, useEventCallback, useLayoutEffect, useLightboxState, isImageSlide, isImageFitCover, round, useController, usePointerEvents, cleanup, makeUseContext, createIcon, IconButton, devicePixelRatio, ImageSlide, clsx, cssClass, addToolbarButton, createModule } from '../../index.js';
2
2
  import * as React from 'react';
3
- import { EVENT_ON_KEY_DOWN, EVENT_ON_WHEEL, EVENT_ON_POINTER_DOWN, EVENT_ON_POINTER_MOVE, EVENT_ON_POINTER_UP, EVENT_ON_POINTER_LEAVE, EVENT_ON_POINTER_CANCEL, UNKNOWN_ACTION_TYPE, CLASS_FULLSIZE, CLASS_FLEX_CENTER, PLUGIN_ZOOM } from '../../types.js';
3
+ import { EVENT_ON_KEY_DOWN, EVENT_ON_WHEEL, UNKNOWN_ACTION_TYPE, CLASS_FULLSIZE, CLASS_FLEX_CENTER, PLUGIN_ZOOM } from '../../types.js';
4
4
 
5
5
  const defaultZoomProps = {
6
6
  maxZoomPixelRatio: 1,
@@ -255,13 +255,14 @@ function useZoomSensors(zoom, maxZoom, disabled, changeZoom, changeOffsets, zoom
255
255
  lastPointerDown.current = 0;
256
256
  pinchZoomDistance.current = undefined;
257
257
  }, []);
258
+ usePointerEvents(subscribeSensors, onPointerDown, onPointerMove, onPointerUp, disabled);
258
259
  React.useEffect(cleanupSensors, [globalIndex, cleanupSensors]);
259
260
  React.useEffect(() => {
260
261
  if (!disabled) {
261
- return cleanup(cleanupSensors, subscribeSensors(EVENT_ON_KEY_DOWN, onKeyDown), subscribeSensors(EVENT_ON_WHEEL, onWheel), subscribeSensors(EVENT_ON_POINTER_DOWN, onPointerDown), subscribeSensors(EVENT_ON_POINTER_MOVE, onPointerMove), subscribeSensors(EVENT_ON_POINTER_UP, onPointerUp), subscribeSensors(EVENT_ON_POINTER_LEAVE, onPointerUp), subscribeSensors(EVENT_ON_POINTER_CANCEL, onPointerUp));
262
+ return cleanup(cleanupSensors, subscribeSensors(EVENT_ON_KEY_DOWN, onKeyDown), subscribeSensors(EVENT_ON_WHEEL, onWheel));
262
263
  }
263
264
  return () => { };
264
- }, [disabled, subscribeSensors, cleanupSensors, onKeyDown, onWheel, onPointerDown, onPointerMove, onPointerUp]);
265
+ }, [disabled, subscribeSensors, cleanupSensors, onKeyDown, onWheel]);
265
266
  }
266
267
 
267
268
  function getCurrentSource(slides, currentIndex) {
package/dist/types.d.ts CHANGED
@@ -12,6 +12,7 @@ declare const PLUGIN_COUNTER = "counter";
12
12
  declare const PLUGIN_DOWNLOAD = "download";
13
13
  declare const PLUGIN_FULLSCREEN = "fullscreen";
14
14
  declare const PLUGIN_INLINE = "inline";
15
+ declare const PLUGIN_SHARE = "share";
15
16
  declare const PLUGIN_SLIDESHOW = "slideshow";
16
17
  declare const PLUGIN_THUMBNAILS = "thumbnails";
17
18
  declare const PLUGIN_ZOOM = "zoom";
@@ -213,6 +214,8 @@ interface ControllerSettings {
213
214
  touchAction: "none" | "pan-y";
214
215
  /** if `true`, set ARIA attributes on the controller div */
215
216
  aria: boolean;
217
+ /** if `true`, close the lightbox on pull-down gesture */
218
+ closeOnPullDown: boolean;
216
219
  /** if `true`, close the lightbox when the backdrop is clicked */
217
220
  closeOnBackdropClick: boolean;
218
221
  }
@@ -403,4 +406,4 @@ type DeepNonNullable<T> = T extends {} ? {
403
406
  [K in keyof T]-?: NonNullable<T[K]>;
404
407
  } : never;
405
408
 
406
- export { ACTION_CLOSE, ACTION_NEXT, ACTION_PREV, ACTION_SWIPE, ACTIVE_SLIDE_COMPLETE, ACTIVE_SLIDE_ERROR, ACTIVE_SLIDE_LOADING, ACTIVE_SLIDE_PLAYING, AnimationSettings, Augmentation, CLASS_FLEX_CENTER, CLASS_FULLSIZE, CLASS_NO_SCROLL, CLASS_NO_SCROLL_PADDING, Callback, Callbacks, CarouselSettings, ClickCallbackProps, Component, ComponentProps, ContainerRect, ControllerRef, ControllerSettings, DeepNonNullable, DeepPartial, DeepPartialValue, ELEMENT_BUTTON, ELEMENT_ICON, EVENT_ON_KEY_DOWN, EVENT_ON_KEY_UP, EVENT_ON_POINTER_CANCEL, EVENT_ON_POINTER_DOWN, EVENT_ON_POINTER_LEAVE, EVENT_ON_POINTER_MOVE, EVENT_ON_POINTER_UP, EVENT_ON_WHEEL, EventTypes, GenericSlide, IMAGE_FIT_CONTAIN, IMAGE_FIT_COVER, ImageFit, ImageSource, Labels, LengthOrPercentage, LightboxExternalProps, LightboxProps, LightboxState, LightboxStateSwipeAction, LightboxStateUpdateAction, MODULE_CAROUSEL, MODULE_CONTROLLER, MODULE_NAVIGATION, MODULE_NO_SCROLL, MODULE_PORTAL, MODULE_ROOT, MODULE_TOOLBAR, Module, NavigationAction, Node, PLUGIN_CAPTIONS, PLUGIN_COUNTER, PLUGIN_DOWNLOAD, PLUGIN_FULLSCREEN, PLUGIN_INLINE, PLUGIN_SLIDESHOW, PLUGIN_THUMBNAILS, PLUGIN_ZOOM, Plugin, PluginProps, PortalSettings, Render, RenderFunction, RenderSlideContainerProps, RenderSlideFooterProps, RenderSlideHeaderProps, RenderSlideProps, SLIDE_STATUS_COMPLETE, SLIDE_STATUS_ERROR, SLIDE_STATUS_LOADING, SLIDE_STATUS_PLACEHOLDER, SLIDE_STATUS_PLAYING, Slide, SlideImage, SlideStatus, SlideTypeKey, SlideTypes, Slot, SlotStyles, SlotType, ToolbarButtonKey, ToolbarButtonKeys, ToolbarSettings, UNKNOWN_ACTION_TYPE, VK_ARROW_LEFT, VK_ARROW_RIGHT, VK_ESCAPE, ViewCallbackProps, activeSlideStatus };
409
+ export { ACTION_CLOSE, ACTION_NEXT, ACTION_PREV, ACTION_SWIPE, ACTIVE_SLIDE_COMPLETE, ACTIVE_SLIDE_ERROR, ACTIVE_SLIDE_LOADING, ACTIVE_SLIDE_PLAYING, AnimationSettings, Augmentation, CLASS_FLEX_CENTER, CLASS_FULLSIZE, CLASS_NO_SCROLL, CLASS_NO_SCROLL_PADDING, Callback, Callbacks, CarouselSettings, ClickCallbackProps, Component, ComponentProps, ContainerRect, ControllerRef, ControllerSettings, DeepNonNullable, DeepPartial, DeepPartialValue, ELEMENT_BUTTON, ELEMENT_ICON, EVENT_ON_KEY_DOWN, EVENT_ON_KEY_UP, EVENT_ON_POINTER_CANCEL, EVENT_ON_POINTER_DOWN, EVENT_ON_POINTER_LEAVE, EVENT_ON_POINTER_MOVE, EVENT_ON_POINTER_UP, EVENT_ON_WHEEL, EventTypes, GenericSlide, IMAGE_FIT_CONTAIN, IMAGE_FIT_COVER, ImageFit, ImageSource, Labels, LengthOrPercentage, LightboxExternalProps, LightboxProps, LightboxState, LightboxStateSwipeAction, LightboxStateUpdateAction, MODULE_CAROUSEL, MODULE_CONTROLLER, MODULE_NAVIGATION, MODULE_NO_SCROLL, MODULE_PORTAL, MODULE_ROOT, MODULE_TOOLBAR, Module, NavigationAction, Node, PLUGIN_CAPTIONS, PLUGIN_COUNTER, PLUGIN_DOWNLOAD, PLUGIN_FULLSCREEN, PLUGIN_INLINE, PLUGIN_SHARE, PLUGIN_SLIDESHOW, PLUGIN_THUMBNAILS, PLUGIN_ZOOM, Plugin, PluginProps, PortalSettings, Render, RenderFunction, RenderSlideContainerProps, RenderSlideFooterProps, RenderSlideHeaderProps, RenderSlideProps, SLIDE_STATUS_COMPLETE, SLIDE_STATUS_ERROR, SLIDE_STATUS_LOADING, SLIDE_STATUS_PLACEHOLDER, SLIDE_STATUS_PLAYING, Slide, SlideImage, SlideStatus, SlideTypeKey, SlideTypes, Slot, SlotStyles, SlotType, ToolbarButtonKey, ToolbarButtonKeys, ToolbarSettings, UNKNOWN_ACTION_TYPE, VK_ARROW_LEFT, VK_ARROW_RIGHT, VK_ESCAPE, ViewCallbackProps, activeSlideStatus };
package/dist/types.js CHANGED
@@ -10,6 +10,7 @@ const PLUGIN_COUNTER = "counter";
10
10
  const PLUGIN_DOWNLOAD = "download";
11
11
  const PLUGIN_FULLSCREEN = "fullscreen";
12
12
  const PLUGIN_INLINE = "inline";
13
+ const PLUGIN_SHARE = "share";
13
14
  const PLUGIN_SLIDESHOW = "slideshow";
14
15
  const PLUGIN_THUMBNAILS = "thumbnails";
15
16
  const PLUGIN_ZOOM = "zoom";
@@ -48,4 +49,4 @@ const IMAGE_FIT_CONTAIN = "contain";
48
49
  const IMAGE_FIT_COVER = "cover";
49
50
  const UNKNOWN_ACTION_TYPE = "Unknown action type";
50
51
 
51
- export { ACTION_CLOSE, ACTION_NEXT, ACTION_PREV, ACTION_SWIPE, ACTIVE_SLIDE_COMPLETE, ACTIVE_SLIDE_ERROR, ACTIVE_SLIDE_LOADING, ACTIVE_SLIDE_PLAYING, CLASS_FLEX_CENTER, CLASS_FULLSIZE, CLASS_NO_SCROLL, CLASS_NO_SCROLL_PADDING, ELEMENT_BUTTON, ELEMENT_ICON, EVENT_ON_KEY_DOWN, EVENT_ON_KEY_UP, EVENT_ON_POINTER_CANCEL, EVENT_ON_POINTER_DOWN, EVENT_ON_POINTER_LEAVE, EVENT_ON_POINTER_MOVE, EVENT_ON_POINTER_UP, EVENT_ON_WHEEL, IMAGE_FIT_CONTAIN, IMAGE_FIT_COVER, MODULE_CAROUSEL, MODULE_CONTROLLER, MODULE_NAVIGATION, MODULE_NO_SCROLL, MODULE_PORTAL, MODULE_ROOT, MODULE_TOOLBAR, PLUGIN_CAPTIONS, PLUGIN_COUNTER, PLUGIN_DOWNLOAD, PLUGIN_FULLSCREEN, PLUGIN_INLINE, PLUGIN_SLIDESHOW, PLUGIN_THUMBNAILS, PLUGIN_ZOOM, SLIDE_STATUS_COMPLETE, SLIDE_STATUS_ERROR, SLIDE_STATUS_LOADING, SLIDE_STATUS_PLACEHOLDER, SLIDE_STATUS_PLAYING, UNKNOWN_ACTION_TYPE, VK_ARROW_LEFT, VK_ARROW_RIGHT, VK_ESCAPE, activeSlideStatus };
52
+ export { ACTION_CLOSE, ACTION_NEXT, ACTION_PREV, ACTION_SWIPE, ACTIVE_SLIDE_COMPLETE, ACTIVE_SLIDE_ERROR, ACTIVE_SLIDE_LOADING, ACTIVE_SLIDE_PLAYING, CLASS_FLEX_CENTER, CLASS_FULLSIZE, CLASS_NO_SCROLL, CLASS_NO_SCROLL_PADDING, ELEMENT_BUTTON, ELEMENT_ICON, EVENT_ON_KEY_DOWN, EVENT_ON_KEY_UP, EVENT_ON_POINTER_CANCEL, EVENT_ON_POINTER_DOWN, EVENT_ON_POINTER_LEAVE, EVENT_ON_POINTER_MOVE, EVENT_ON_POINTER_UP, EVENT_ON_WHEEL, IMAGE_FIT_CONTAIN, IMAGE_FIT_COVER, MODULE_CAROUSEL, MODULE_CONTROLLER, MODULE_NAVIGATION, MODULE_NO_SCROLL, MODULE_PORTAL, MODULE_ROOT, MODULE_TOOLBAR, PLUGIN_CAPTIONS, PLUGIN_COUNTER, PLUGIN_DOWNLOAD, PLUGIN_FULLSCREEN, PLUGIN_INLINE, PLUGIN_SHARE, PLUGIN_SLIDESHOW, PLUGIN_THUMBNAILS, PLUGIN_ZOOM, SLIDE_STATUS_COMPLETE, SLIDE_STATUS_ERROR, SLIDE_STATUS_LOADING, SLIDE_STATUS_PLACEHOLDER, SLIDE_STATUS_PLAYING, UNKNOWN_ACTION_TYPE, VK_ARROW_LEFT, VK_ARROW_RIGHT, VK_ESCAPE, activeSlideStatus };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yet-another-react-lightbox",
3
- "version": "3.6.0",
3
+ "version": "3.8.0",
4
4
  "description": "Modern React lightbox component",
5
5
  "author": "Igor Danchenko",
6
6
  "license": "MIT",
@@ -52,6 +52,10 @@
52
52
  "types": "./dist/plugins/inline/index.d.ts",
53
53
  "default": "./dist/plugins/inline/index.js"
54
54
  },
55
+ "./plugins/share": {
56
+ "types": "./dist/plugins/share/index.d.ts",
57
+ "default": "./dist/plugins/share/index.js"
58
+ },
55
59
  "./plugins/slideshow": {
56
60
  "types": "./dist/plugins/slideshow/index.d.ts",
57
61
  "default": "./dist/plugins/slideshow/index.js"
@@ -108,6 +112,9 @@
108
112
  "plugins/inline": [
109
113
  "dist/plugins/inline/index.d.ts"
110
114
  ],
115
+ "plugins/share": [
116
+ "dist/plugins/share/index.d.ts"
117
+ ],
111
118
  "plugins/slideshow": [
112
119
  "dist/plugins/slideshow/index.d.ts"
113
120
  ],