playbook_ui 10.21.0.pre.alpha.jg1 → 10.21.0.pre.alpha.lightbox

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/_playbook.scss +1 -0
  3. data/app/pb_kits/playbook/data/menu.yml +1 -0
  4. data/app/pb_kits/playbook/index.js +2 -1
  5. data/app/pb_kits/playbook/pb_avatar/_avatar.jsx +1 -1
  6. data/app/pb_kits/playbook/pb_avatar/_avatar.scss +2 -2
  7. data/app/pb_kits/playbook/pb_avatar/avatar.rb +1 -1
  8. data/app/pb_kits/playbook/pb_avatar/avatar.test.js +1 -1
  9. data/app/pb_kits/playbook/pb_background/_background.jsx +2 -7
  10. data/app/pb_kits/playbook/pb_background/_background.scss +8 -24
  11. data/app/pb_kits/playbook/pb_background/background.rb +6 -6
  12. data/app/pb_kits/playbook/pb_background/docs/_background_image.html.erb +2 -50
  13. data/app/pb_kits/playbook/pb_background/docs/_background_image.jsx +28 -71
  14. data/app/pb_kits/playbook/pb_bar_graph/_bar_graph.jsx +4 -0
  15. data/app/pb_kits/playbook/pb_bar_graph/bar_graph.rb +1 -0
  16. data/app/pb_kits/playbook/pb_button/_button.jsx +3 -3
  17. data/app/pb_kits/playbook/pb_button/_button.scss +18 -1
  18. data/app/pb_kits/playbook/pb_button/button.rb +11 -3
  19. data/app/pb_kits/playbook/pb_button/button.test.js +13 -0
  20. data/app/pb_kits/playbook/pb_button/docs/_button_size.html.erb +3 -0
  21. data/app/pb_kits/playbook/pb_button/docs/_button_size.jsx +26 -0
  22. data/app/pb_kits/playbook/pb_button/docs/_button_size.md +1 -0
  23. data/app/pb_kits/playbook/pb_button/docs/example.yml +2 -0
  24. data/app/pb_kits/playbook/pb_button/docs/index.js +1 -0
  25. data/app/pb_kits/playbook/pb_circle_chart/_circle_chart.jsx +3 -0
  26. data/app/pb_kits/playbook/pb_circle_chart/circle_chart.rb +1 -0
  27. data/app/pb_kits/playbook/pb_dashboard/pbChartsDarkTheme.js +215 -0
  28. data/app/pb_kits/playbook/pb_file_upload/_file_upload.jsx +17 -10
  29. data/app/pb_kits/playbook/pb_file_upload/fileupload.test.js +40 -0
  30. data/app/pb_kits/playbook/pb_gauge/_gauge.jsx +3 -0
  31. data/app/pb_kits/playbook/pb_gauge/gauge.rb +1 -0
  32. data/app/pb_kits/playbook/pb_image/_image.jsx +1 -1
  33. data/app/pb_kits/playbook/pb_image/_image.scss +3 -3
  34. data/app/pb_kits/playbook/pb_image/image.rb +1 -1
  35. data/app/pb_kits/playbook/pb_image/image.test.js +1 -1
  36. data/app/pb_kits/playbook/pb_lightbox/Carousel/Slide.jsx +55 -0
  37. data/app/pb_kits/playbook/pb_lightbox/Carousel/Slides.jsx +54 -0
  38. data/app/pb_kits/playbook/pb_lightbox/Carousel/Thumbnail.jsx +39 -0
  39. data/app/pb_kits/playbook/pb_lightbox/Carousel/Thumbnails.jsx +82 -0
  40. data/app/pb_kits/playbook/pb_lightbox/Carousel/index.jsx +59 -0
  41. data/app/pb_kits/playbook/pb_lightbox/Carousel/styles.scss +110 -0
  42. data/app/pb_kits/playbook/pb_lightbox/Carousel/useSlides.js +66 -0
  43. data/app/pb_kits/playbook/pb_lightbox/_lightbox.jsx +112 -0
  44. data/app/pb_kits/playbook/pb_lightbox/_lightbox_context.jsx +3 -0
  45. data/app/pb_kits/playbook/pb_lightbox/_lightbox_header.jsx +71 -0
  46. data/app/pb_kits/playbook/pb_lightbox/_lightbox_header_icon.jsx +26 -0
  47. data/app/pb_kits/playbook/pb_lightbox/docs/_lightbox_compound_component.jsx +95 -0
  48. data/app/pb_kits/playbook/pb_lightbox/docs/_lightbox_default.jsx +64 -0
  49. data/app/pb_kits/playbook/pb_lightbox/docs/_lightbox_default.md +1 -0
  50. data/app/pb_kits/playbook/pb_lightbox/docs/_lightbox_multiple.jsx +64 -0
  51. data/app/pb_kits/playbook/pb_lightbox/docs/example.yml +7 -0
  52. data/app/pb_kits/playbook/pb_lightbox/docs/index.js +3 -0
  53. data/app/pb_kits/playbook/pb_lightbox/hooks/useVisibility.js +21 -0
  54. data/app/pb_kits/playbook/pb_lightbox/hooks/useWindowSize.js +25 -0
  55. data/app/pb_kits/playbook/pb_lightbox/lightbox.scss +90 -0
  56. data/app/pb_kits/playbook/pb_lightbox/lightbox.test.jsx +30 -0
  57. data/app/pb_kits/playbook/pb_line_graph/_line_graph.jsx +4 -0
  58. data/app/pb_kits/playbook/pb_line_graph/line_graph.rb +1 -0
  59. data/app/pb_kits/playbook/pb_popover/_popover.jsx +2 -4
  60. data/app/pb_kits/playbook/pb_popover/docs/_popover_close.html.erb +7 -7
  61. data/app/pb_kits/playbook/pb_popover/index.js +4 -9
  62. data/app/pb_kits/playbook/pb_popover/popover.html.erb +1 -1
  63. data/app/pb_kits/playbook/pb_rich_text_editor/_rich_text_editor.jsx +4 -0
  64. data/app/pb_kits/playbook/pb_text_input/_text_input.scss +2 -2
  65. data/app/pb_kits/playbook/pb_text_input/text_input.test.js +14 -0
  66. data/app/pb_kits/playbook/playbook-doc.js +2 -0
  67. data/app/pb_kits/playbook/plugins/pb_chart.js +8 -4
  68. data/lib/playbook/version.rb +2 -2
  69. metadata +28 -3
  70. data/app/pb_kits/playbook/pb_background/docs/_background_image.md +0 -1
@@ -0,0 +1,110 @@
1
+ .Lightbox {
2
+ width: 100vw;
3
+ height: 100vh;
4
+ position: fixed;
5
+ left: 0;
6
+ top: 0;
7
+ display: flex;
8
+ align-items: center;
9
+ flex-direction: column;
10
+ background-color: black;
11
+ z-index: 1;
12
+ overflow: hidden;
13
+ }
14
+
15
+ .Slides {
16
+ display: flex;
17
+ flex-grow: 1;
18
+ height: calc(100% - 100px);
19
+ width: 100%;
20
+ z-index: 1;
21
+
22
+ [class^="react-transform-wrapper"] {
23
+ flex-shrink: 0;
24
+ width: 100%;
25
+ height: 100%;
26
+ }
27
+
28
+ [class^="react-transform-content"] {
29
+ width: 100%;
30
+ height: 100%;
31
+ }
32
+ }
33
+
34
+ .Slide,
35
+ .Thumbnail {
36
+ flex-shrink: 0;
37
+ border: none;
38
+ margin: 0;
39
+ padding: 0;
40
+ cursor: pointer;
41
+ background-color: transparent;
42
+ }
43
+
44
+ .Slide {
45
+ display: flex;
46
+ justify-content: center;
47
+ align-items: center;
48
+ width: 100%;
49
+ height: 100%;
50
+ overflow: hidden;
51
+
52
+ img {
53
+ width: 100vw;
54
+ height: 100vh;
55
+ object-fit: contain;
56
+ }
57
+ }
58
+
59
+ .BackBtn,
60
+ .NextBtn {
61
+ z-index: 2;
62
+ color: black;
63
+ position: absolute;
64
+ width: 50px;
65
+ height: 50px;
66
+ top: calc(50vh - 5px);
67
+ border: none;
68
+ border-radius: 50%;
69
+ background-color: white;
70
+ }
71
+
72
+ .BackBtn::before,
73
+ .NextBtn::before {
74
+ content: "▸";
75
+ }
76
+
77
+ .BackBtn {
78
+ left: 30px;
79
+ transform: rotate(180deg);
80
+ }
81
+
82
+ .NextBtn {
83
+ right: 30px;
84
+ }
85
+
86
+ .Thumbnails {
87
+ display: flex;
88
+ padding: 3px;
89
+ }
90
+
91
+ .Thumbnails.draggable {
92
+ align-self: flex-start;
93
+ }
94
+
95
+ .Thumbnail {
96
+ padding: 3px;
97
+ height: 100%;
98
+
99
+ img {
100
+ width: 100%;
101
+ height: 100%;
102
+ }
103
+ }
104
+
105
+ .Thumbnail.active {
106
+ padding: 6px;
107
+ background-color: white;
108
+ box-shadow: 0 0 6px white;
109
+ }
110
+
@@ -0,0 +1,66 @@
1
+ // @flow
2
+
3
+ import { noop } from 'lodash'
4
+ import { useEffect, useState } from 'react'
5
+ import { useAnimation } from 'framer-motion'
6
+ import { useWindowSize } from '../hooks/useWindowSize'
7
+
8
+ const cycleIndex = (current: number, min: number, max: number): number => {
9
+ if (current < min) return max
10
+ if (current > max) return min
11
+ return current
12
+ }
13
+
14
+ const swipeConfidenceThreshold = 10000
15
+ const swipePower = (offset: number, velocity: number) => {
16
+ return Math.abs(offset) * velocity
17
+ }
18
+
19
+ export default function useSlides({
20
+ current = 0,
21
+ pagesCount = 0,
22
+ onChange = noop,
23
+ }) {
24
+ const controls = useAnimation()
25
+ const viewportSize = useWindowSize()
26
+ const [currentIndex, setCurrentIndex] = useState(current)
27
+ const dragConstraints = {
28
+ left: -viewportSize.width * (pagesCount - 1),
29
+ right: 0,
30
+ }
31
+
32
+ const paginate = (newDirection: number) => {
33
+ const nextIndex = currentIndex + newDirection
34
+ const cycledNextIndex = cycleIndex(nextIndex, 0, pagesCount - 1)
35
+ setCurrentIndex(cycledNextIndex)
36
+ return cycledNextIndex
37
+ }
38
+
39
+ const handleDragEnd = (e, { offset, velocity }) => {
40
+ let nextIndex = currentIndex
41
+ const swipe = swipePower(offset.x, velocity.x)
42
+
43
+ if (swipe < -swipeConfidenceThreshold) {
44
+ nextIndex = paginate(1)
45
+ } else if (swipe > swipeConfidenceThreshold) {
46
+ nextIndex = paginate(-1)
47
+ }
48
+
49
+ controls.start({ x: -viewportSize.width * nextIndex })
50
+
51
+ if (nextIndex !== currentIndex) {
52
+ onChange(nextIndex)
53
+ }
54
+ }
55
+
56
+ useEffect(() => {
57
+ controls.set({ x: -viewportSize.width * current })
58
+ setCurrentIndex(current)
59
+ }, [controls, current, viewportSize.width])
60
+
61
+ return {
62
+ controls,
63
+ dragConstraints,
64
+ handleDragEnd,
65
+ }
66
+ }
@@ -0,0 +1,112 @@
1
+ /* eslint-disable no-unused-vars */
2
+ /* @flow */
3
+
4
+ import React, { useState } from 'react'
5
+ import classnames from 'classnames'
6
+ import { buildAriaProps, buildCss, buildDataProps } from '../utilities/props.js'
7
+ import { globalProps } from '../utilities/globalProps.js'
8
+ import Icon from '../pb_icon/_icon'
9
+ import LightboxHeader from './_lightbox_header'
10
+ import { LightboxContext } from './_lightbox_context'
11
+
12
+ import Carousel from './Carousel/index.jsx'
13
+
14
+ type LightboxType = {
15
+ aria?: object,
16
+ children: array<React.ReactNode> | React.ReactNode | string,
17
+ className?: string,
18
+ data?: object,
19
+ description?: string,
20
+ id?: string,
21
+ photos: [],
22
+ initialPhoto: string,
23
+ onClose: Function,
24
+ opened: boolean,
25
+ icon: string,
26
+ iconSize: number,
27
+ trigger?: string,
28
+ }
29
+
30
+ const Lightbox = (props: LightboxType) => {
31
+ const {
32
+ aria = {},
33
+ children,
34
+ className,
35
+ data = {},
36
+ description,
37
+ id = '',
38
+ photos,
39
+ initialPhoto,
40
+ onClose,
41
+ opened,
42
+ icon,
43
+ iconSize,
44
+ trigger,
45
+ } = props
46
+ const [activePhoto, setActivePhoto] = useState(initialPhoto)
47
+
48
+ const ariaProps = buildAriaProps(aria)
49
+ const dataProps = buildDataProps(data)
50
+ const classes = classnames(
51
+ buildCss('pb_lightbox_kit'),
52
+ globalProps(props),
53
+ className
54
+ )
55
+
56
+ const handleOnSlide = (index) => {
57
+ setActivePhoto(photos[index])
58
+ }
59
+
60
+ const api = {
61
+ onClose: trigger
62
+ ? function () {
63
+ setTriggerOpened(false)
64
+ }
65
+ : onClose,
66
+ }
67
+
68
+ const [triggerOpened, setTriggerOpened] = useState(false),
69
+ modalIsOpened = trigger ? triggerOpened : opened
70
+
71
+ if (trigger) {
72
+ const modalTrigger = document.querySelector(trigger)
73
+ modalTrigger.addEventListener(
74
+ 'click',
75
+ () => {
76
+ setTriggerOpened(true)
77
+ document
78
+ .querySelector('#cancel-button')
79
+ .addEventListener('click', () => {
80
+ setTriggerOpened(false)
81
+ })
82
+ },
83
+ { once: true }
84
+ )
85
+ }
86
+ return (
87
+ <LightboxContext.Provider value={api}>
88
+ <div
89
+ {...ariaProps}
90
+ {...dataProps}
91
+ className={classes}
92
+ id={id}
93
+ >
94
+ <div className="carousel">
95
+ <Lightbox.Header>{children}</Lightbox.Header>
96
+ <Carousel
97
+ current={photos.indexOf(initialPhoto)}
98
+ onChange={handleOnSlide}
99
+ photos={photos.map((photo) => ({
100
+ url: photo,
101
+ thumbnail: photo,
102
+ }))}
103
+ />
104
+ </div>
105
+ </div>
106
+ </LightboxContext.Provider>
107
+ )
108
+ }
109
+
110
+ Lightbox.Header = LightboxHeader
111
+
112
+ export default Lightbox
@@ -0,0 +1,3 @@
1
+ import React from 'react'
2
+
3
+ export const LightboxContext = React.createContext()
@@ -0,0 +1,71 @@
1
+ /* @flow */
2
+ import React, { useContext } from 'react'
3
+ import classnames from 'classnames'
4
+ import { buildAriaProps, buildCss, buildDataProps } from '../utilities/props'
5
+ import { globalProps } from '../utilities/globalProps.js'
6
+ import { LightboxContext } from './_lightbox_context'
7
+ import { LightboxHeaderIcon } from './_lightbox_header_icon'
8
+ import Flex from '../pb_flex/_flex'
9
+ import FlexItem from '../pb_flex/_flex_item'
10
+
11
+ type LightboxHeaderProps = {
12
+ aria?: object,
13
+ children: array<React.ReactNode> | React.ReactNode | string,
14
+ className?: string,
15
+ closeable: boolean,
16
+ data?: object,
17
+ icon?: string,
18
+ iconSize?: string,
19
+ id?: string,
20
+ padding?: string,
21
+ spacing?: string,
22
+ text?: string,
23
+ title?: string,
24
+ }
25
+
26
+ const LightboxHeader = (props: LightboxHeaderProps) => {
27
+ const {
28
+ aria = {},
29
+ children,
30
+ className,
31
+ data = {},
32
+ padding = 'sm',
33
+ spacing = 'between',
34
+ closeable = true,
35
+ icon = 'times',
36
+ iconSize = '2x',
37
+ } = props
38
+
39
+ const ariaProps = buildAriaProps(aria)
40
+ const dataProps = buildDataProps(data)
41
+ const api = useContext(LightboxContext)
42
+ const headerCSS = buildCss('lightbox_header')
43
+ const headerSpacing = globalProps(props, { padding })
44
+
45
+ /* eslint-disable react/jsx-handler-names */
46
+
47
+ return (
48
+ <div className="carousel-header">
49
+ <Flex
50
+ {...ariaProps}
51
+ {...dataProps}
52
+ className={classnames(headerCSS, headerSpacing, className)}
53
+ spacing={spacing}
54
+ >
55
+ <If condition={closeable}>
56
+ <FlexItem flex={1}>
57
+ <LightboxHeaderIcon
58
+ className="close-icon"
59
+ icon={icon}
60
+ iconSize={iconSize}
61
+ onClose={api.onClose}
62
+ />
63
+ </FlexItem>
64
+ </If>
65
+ {children}
66
+ </Flex>
67
+ </div>
68
+ )
69
+ }
70
+
71
+ export default LightboxHeader
@@ -0,0 +1,26 @@
1
+ /* @flow */
2
+
3
+ import React from 'react'
4
+ import Icon from '../pb_icon/_icon'
5
+
6
+ type LightboxHeaderIconProps = {
7
+ onClose: () => mixed,
8
+ icon: string,
9
+ iconSize: string,
10
+ }
11
+
12
+ export const LightboxHeaderIcon = (props: LightboxHeaderIconProps) => {
13
+ const { onClose, icon, iconSize } = props
14
+ return (
15
+ <div
16
+ className="close-icon"
17
+ onClick={onClose}
18
+ >
19
+ <Icon
20
+ fixedWidth
21
+ icon={icon}
22
+ size={iconSize}
23
+ />
24
+ </div>
25
+ )
26
+ }
@@ -0,0 +1,95 @@
1
+ /* @flow */
2
+ /* eslint-disable jsx-control-statements/jsx-use-if-tag */
3
+ import React, { useState } from 'react'
4
+ import { Caption, Flex, FlexItem, Image, Title } from '../../'
5
+ import Lightbox from '../_lightbox'
6
+
7
+ const LightboxCompoundComponent = (props) => {
8
+ const photos = [
9
+ 'https://images.unsplash.com/photo-1638727228877-d2a79ab75e68?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2668&q=80',
10
+ 'https://images.unsplash.com/photo-1501045337096-542a73dafa4f?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2052&q=80',
11
+ 'https://images.unsplash.com/photo-1563693998336-93c10e5d8f91?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1470&q=80,',
12
+ ]
13
+ const [selectedPhoto, setSelectedPhoto] = useState(photos[0])
14
+ const [showLightbox, toggleShowLightbox] = useState(false)
15
+
16
+ const handleCloseLightbox = () => {
17
+ toggleShowLightbox(!showLightbox)
18
+ setSelectedPhoto(null)
19
+ }
20
+
21
+ const onPhotoClick = (photo) => {
22
+ toggleShowLightbox(!showLightbox)
23
+ setSelectedPhoto(photo)
24
+ }
25
+
26
+ return (
27
+ <>
28
+ <div>
29
+ {showLightbox ? (
30
+ <Lightbox
31
+ icon="times"
32
+ iconSize="2x"
33
+ initialPhoto={selectedPhoto}
34
+ onClose={handleCloseLightbox}
35
+ photos={photos}
36
+ {...props}
37
+ >
38
+ <Lightbox.Header>
39
+ <FlexItem flex={5}>
40
+ <Flex justify="center">
41
+ <Flex
42
+ align="center"
43
+ orientation="column"
44
+ >
45
+ <Title
46
+ dark
47
+ paddingBottom="xs"
48
+ size={4}
49
+ text="Windows, Sidings, & Gutters"
50
+ />
51
+
52
+ <Caption dark>{'Dyamic Count goes here.'}</Caption>
53
+ </Flex>
54
+ </Flex>
55
+ </FlexItem>
56
+ <FlexItem flex={1}>
57
+ <Flex justify="end">
58
+ <Title
59
+ dark
60
+ size={4}
61
+ text="All Photos"
62
+ />
63
+ </Flex>
64
+ </FlexItem>
65
+ </Lightbox.Header>
66
+ </Lightbox>
67
+ ) : (
68
+ <div className="PhotoViewer">
69
+ <Flex>
70
+ {photos.map((photo, index) => {
71
+ return (
72
+ <div
73
+ key={photo[index]}
74
+ onClick={() => onPhotoClick(photo)}
75
+ >
76
+ <Image
77
+ marginRight="xl"
78
+ rounded
79
+ size="lg"
80
+ url={photo}
81
+ />
82
+
83
+ <div className="overlay" />
84
+ </div>
85
+ )
86
+ })}
87
+ </Flex>
88
+ </div>
89
+ )}
90
+ </div>
91
+ </>
92
+ )
93
+ }
94
+
95
+ export default LightboxCompoundComponent
@@ -0,0 +1,64 @@
1
+ /* @flow */
2
+ /* eslint-disable jsx-control-statements/jsx-use-if-tag */
3
+ import React, { useState } from 'react'
4
+ import { Flex, Image } from '../../'
5
+ import Lightbox from '../_lightbox'
6
+
7
+ const LightboxDefault = (props) => {
8
+ const photos = [
9
+ 'https://images.unsplash.com/photo-1638727228877-d2a79ab75e68?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2668&q=80',
10
+ ]
11
+ const [selectedPhoto, setSelectedPhoto] = useState(photos[0])
12
+ const [showLightbox, toggleShowLightbox] = useState(false)
13
+
14
+ const handleCloseLightbox = () => {
15
+ toggleShowLightbox(!showLightbox)
16
+ setSelectedPhoto(null)
17
+ }
18
+
19
+ const onPhotoClick = (photo) => {
20
+ toggleShowLightbox(!showLightbox)
21
+ setSelectedPhoto(photo)
22
+ }
23
+
24
+ return (
25
+ <>
26
+ <div>
27
+ {showLightbox ? (
28
+ <Lightbox
29
+ icon="times"
30
+ iconSize="2x"
31
+ initialPhoto={selectedPhoto}
32
+ onClose={handleCloseLightbox}
33
+ photos={photos}
34
+ {...props}
35
+ />
36
+ ) : (
37
+ <div className="PhotoViewer">
38
+ <Flex>
39
+ {photos.map((photo, index) => {
40
+ return (
41
+ <div
42
+ key={photo[index]}
43
+ onClick={() => onPhotoClick(photo)}
44
+ >
45
+ <Image
46
+ marginRight="xl"
47
+ rounded
48
+ size="lg"
49
+ url={photo}
50
+ />
51
+
52
+ <div className="overlay" />
53
+ </div>
54
+ )
55
+ })}
56
+ </Flex>
57
+ </div>
58
+ )}
59
+ </div>
60
+ </>
61
+ )
62
+ }
63
+
64
+ export default LightboxDefault
@@ -0,0 +1 @@
1
+ Lightbox contains 5 props: `photos` (an array of urls), `initialPhoto` (a number ), an `onClose` callback function, an `icon` prop that matches to our `Icon` kit, and an `iconSize` prop that also corresponds to our icon kit.
@@ -0,0 +1,64 @@
1
+ /* @flow */
2
+ /* eslint-disable jsx-control-statements/jsx-use-if-tag */
3
+ import React, { useState } from 'react'
4
+ import { Flex, Image } from '../../'
5
+ import Lightbox from '../_lightbox.jsx'
6
+
7
+ const LightboxMultiple = (props) => {
8
+ const photos = [
9
+ 'https://images.unsplash.com/photo-1638727228877-d2a79ab75e68?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2668&q=80',
10
+ 'https://images.unsplash.com/photo-1526657782461-9fe13402a841?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1984&q=80',
11
+ 'https://images.unsplash.com/photo-1523057530100-383d7fbc77a1?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2669&q=80',
12
+ ]
13
+ const [selectedPhoto, setSelectedPhoto] = useState(photos[0])
14
+ const [light, toggleLight] = useState(false)
15
+
16
+ const handleCloseLightbox = () => {
17
+ toggleLight(!light)
18
+ setSelectedPhoto(null)
19
+ }
20
+
21
+ const onPhotoClick = (photo) => {
22
+ toggleLight(!light)
23
+ setSelectedPhoto(photo)
24
+ }
25
+
26
+ return (
27
+ <div>
28
+ {light ? (
29
+ <Lightbox
30
+ icon="times"
31
+ iconSize="2x"
32
+ initialPhoto={selectedPhoto}
33
+ onClose={handleCloseLightbox}
34
+ photos={photos}
35
+ {...props}
36
+ />
37
+ ) : (
38
+ <div className="PhotoViewer">
39
+ <Flex>
40
+ {photos.map((photo, index) => {
41
+ return (
42
+ <div
43
+ key={photos[index]}
44
+ onClick={() => onPhotoClick(photo)}
45
+ >
46
+ <Image
47
+ marginRight="xl"
48
+ rounded
49
+ size="lg"
50
+ url={photo}
51
+ />
52
+
53
+ <div className="overlay" />
54
+ </div>
55
+ )
56
+ })}
57
+ </Flex>
58
+ </div>
59
+ )}
60
+ </div>
61
+ )
62
+ }
63
+
64
+ export default LightboxMultiple
@@ -0,0 +1,7 @@
1
+ examples:
2
+
3
+ react:
4
+ - lightbox_default: Default
5
+ - lightbox_multiple: Multiple
6
+ - lightbox_compound_component: Compound Component
7
+
@@ -0,0 +1,3 @@
1
+ export { default as LightboxDefault } from './_lightbox_default.jsx'
2
+ export { default as LightboxMultiple } from './_lightbox_multiple.jsx'
3
+ export { default as LightboxCompoundComponent } from './_lightbox_compound_component.jsx'
@@ -0,0 +1,21 @@
1
+ import { debounce } from 'lodash'
2
+ import { useCallback, useMemo, useState } from 'react'
3
+
4
+ export default function useVisibility(initialState = false) {
5
+ const [visible, setVisible] = useState(initialState)
6
+ const hide = useCallback(() => setVisible(false), [])
7
+ const show = useCallback(({ afterDelay = 0 } = {}) => {
8
+ debounce(() => setVisible(true), afterDelay)()
9
+ }, [])
10
+ const toggle = useCallback(() => setVisible((current) => !current), [])
11
+
12
+ return useMemo(
13
+ () => ({
14
+ hide,
15
+ show,
16
+ toggle,
17
+ visible,
18
+ }),
19
+ [hide, show, toggle, visible]
20
+ )
21
+ }
@@ -0,0 +1,25 @@
1
+ import { useEffect, useState } from 'react'
2
+
3
+ export const useWindowSize = () => {
4
+ const [size, setSize] = useState({
5
+ width: window.innerWidth,
6
+ height: window.innerHeight,
7
+ })
8
+
9
+ useEffect(() => {
10
+ const handleResize = () => {
11
+ setSize({
12
+ width: window.innerWidth,
13
+ height: window.innerHeight,
14
+ })
15
+ }
16
+
17
+ window.addEventListener('resize', handleResize)
18
+
19
+ handleResize()
20
+
21
+ return () => window.removeEventListener('resize', handleResize)
22
+ }, [])
23
+
24
+ return size
25
+ }