Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: jossmac/react-images
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v0.5.6
Choose a base ref
...
head repository: jossmac/react-images
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: master
Choose a head ref

Commits on Oct 3, 2017

  1. Copy the full SHA
    41d5168 View commit details
  2. build 0.5.7

    neptunian committed Oct 3, 2017
    Copy the full SHA
    2373da3 View commit details

Commits on Oct 6, 2017

  1. Copy the full SHA
    f2b123a View commit details
  2. Merge pull request #169 from d-fischer/fix-react-dependency

    fix react and react-dom peerDependencies
    neptunian authored Oct 6, 2017
    Copy the full SHA
    75c09fe View commit details
  3. Copy the full SHA
    e6343e9 View commit details

Commits on Oct 11, 2017

  1. Copy the full SHA
    8e186d1 View commit details
  2. update dist files

    neptunian committed Oct 11, 2017
    Copy the full SHA
    19d4303 View commit details
  3. update dist files

    neptunian committed Oct 11, 2017
    Copy the full SHA
    0954fcb View commit details
  4. update dist files

    neptunian committed Oct 11, 2017
    Copy the full SHA
    cb071ed View commit details
  5. remove swp files

    neptunian committed Oct 11, 2017
    Copy the full SHA
    f5d3fe5 View commit details
  6. revert dist changes

    neptunian committed Oct 11, 2017
    Copy the full SHA
    84ed393 View commit details

Commits on Oct 15, 2017

  1. add yarn

    neptunian committed Oct 15, 2017
    Copy the full SHA
    009c534 View commit details

Commits on Oct 18, 2017

  1. new build system

    neptunian committed Oct 18, 2017
    Copy the full SHA
    1bffdd2 View commit details
  2. Copy the full SHA
    243c98e View commit details

Commits on Oct 20, 2017

  1. Unverified

    This commit is not signed, but one or more authors requires that any commit attributed to them is signed.
    Copy the full SHA
    419e94f View commit details

Commits on Nov 27, 2017

  1. Merge pull request #172 from kachkaev/patch-1

    Change the way onClickImage is propagated
    neptunian authored Nov 27, 2017

    Verified

    This commit was created on github.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    dab4ced View commit details

Commits on Nov 29, 2017

  1. bump version

    neptunian committed Nov 29, 2017
    Copy the full SHA
    d7336c4 View commit details
  2. update build files

    neptunian committed Nov 29, 2017
    Copy the full SHA
    ccf9ee5 View commit details
  3. Copy the full SHA
    948ae59 View commit details
  4. update build files

    neptunian committed Nov 29, 2017
    Copy the full SHA
    8ce980d View commit details
  5. bump version to 0.5.13

    neptunian committed Nov 29, 2017
    Copy the full SHA
    db5236d View commit details

Commits on Dec 6, 2017

  1. Unmount the react portal tree when unmounting the Portal component.

    In addition to removing the portal element from the document we also need to unmount the React tree, otherwise these references are kept in memory.
    pleunv authored Dec 6, 2017

    Verified

    This commit was created on github.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    798151d View commit details

Commits on Dec 15, 2017

  1. add npm badge to readme

    jossmac authored Dec 15, 2017

    Verified

    This commit was created on github.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    a87edfd View commit details

Commits on Dec 21, 2017

  1. [readme] Documented "alt" property of images.

    It is already part of the code since 2b61331 and version 0.5.3.
    apepper committed Dec 21, 2017
    Copy the full SHA
    91af58a View commit details

Commits on Dec 27, 2017

  1. Implement loading spinner, improve UX for slow internet connection (#187

    )
    
    * Implement loading spinner, improve UX for slow internet connection
    * Fix srcset preloading if it is not set
    * Fix blink of spinner on fast internet connection
    mkalygin authored and jossmac committed Dec 27, 2017
    Copy the full SHA
    d67f857 View commit details

Commits on Dec 28, 2017

  1. v0.5.14

    jossmac committed Dec 28, 2017
    Copy the full SHA
    3d48f47 View commit details

Commits on Dec 29, 2017

  1. Copy the full SHA
    bae52b7 View commit details

Commits on Jan 25, 2018

  1. update build files

    neptunian committed Jan 25, 2018
    Copy the full SHA
    98ef572 View commit details
  2. Merge pull request #190 from jorrit/reactscrolllockupdate

    Update react-scrolllock to 2.0.1
    neptunian authored Jan 25, 2018

    Verified

    This commit was created on github.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    7bd7ff6 View commit details
  3. Merge pull request #186 from apepper/altReadme

    README.md: Documented "alt" property of images
    neptunian authored Jan 25, 2018

    Verified

    This commit was created on github.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    45d097c View commit details
  4. Merge pull request #180 from pleunv/master

    Unmount the component tree before removing the portal node from DOM.
    neptunian authored Jan 25, 2018

    Verified

    This commit was created on github.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    34924d2 View commit details

Commits on Jan 27, 2018

  1. v1 initial commit;

    react-create-app until build setup
    jossmac committed Jan 27, 2018
    Copy the full SHA
    8718c85 View commit details
  2. lightbox componentdidmount currentImage added

    ilker0 committed Jan 27, 2018
    Copy the full SHA
    796c344 View commit details

Commits on Jan 28, 2018

  1. Copy the full SHA
    dc88ce0 View commit details

Commits on Jan 29, 2018

  1. Merge pull request #191 from jorrit/yarn

    Update documentation and example for yarn
    neptunian authored Jan 29, 2018

    Verified

    This commit was created on github.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    45d11e5 View commit details
  2. Merge pull request #200 from ilker0/lightbox

    lightbox componentdidmount currentImage added
    neptunian authored Jan 29, 2018

    Verified

    This commit was created on github.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    cc2634f View commit details

Commits on Jan 30, 2018

  1. Copy the full SHA
    0862100 View commit details
  2. Copy the full SHA
    7f229b1 View commit details

Commits on Feb 1, 2018

  1. Quick fix for "srcSet" inconsistency (#204)

    Fixes #203
    kripod authored and jossmac committed Feb 1, 2018

    Unverified

    This commit is not signed, but one or more authors requires that any commit attributed to them is signed.
    Copy the full SHA
    5ce37ad View commit details

Commits on Feb 5, 2018

  1. Allow scroll lock to be enabled/disabled via preventScroll prop (#188)

    * make prevent scroll optional
    Josh-a-e authored and jossmac committed Feb 5, 2018
    Copy the full SHA
    06f7a38 View commit details

Commits on Feb 7, 2018

  1. Copy the full SHA
    4c06d48 View commit details
  2. Fix #202: remove react-spinners dependency (#208)

    * Fix #202: remove react-spinners dependency
    * Rename example custom spinner CSS class
    mkalygin authored and jossmac committed Feb 7, 2018
    Copy the full SHA
    a42ffca View commit details
  3. Copy the full SHA
    1a2af89 View commit details
  4. cleanup srcSet

    jossmac committed Feb 7, 2018
    Copy the full SHA
    a99a878 View commit details
  5. 0.5.17

    jossmac committed Feb 7, 2018
    Copy the full SHA
    60778d1 View commit details
  6. Verified

    This commit was created on github.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    3c29954 View commit details

Commits on Feb 12, 2018

  1. Verified

    This commit was created on github.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    f6e75ce View commit details

Commits on Feb 13, 2018

  1. V1 build system & style framework (#211)

    * rollup
    * move to commonProps + innerProps pattern
    * implement dynamic styling
    * add formatCount prop
    jossmac authored Feb 13, 2018

    Verified

    This commit was created on github.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    edcc476 View commit details

Commits on Feb 14, 2018

  1. Copy the full SHA
    666b00d View commit details
  2. Copy the full SHA
    e740766 View commit details
Showing with 13,895 additions and 43,668 deletions.
  1. +10 −0 .babelrc
  2. +14 −0 .editorconfig
  3. +1 −0 .env
  4. +4 −0 .eslintignore
  5. +47 −4 .eslintrc
  6. +11 −0 .flowconfig
  7. +13 −12 .github/CONTRIBUTING.md
  8. +123 −46 .github/HISTORY.md
  9. +1 −3 .github/PULL_REQUEST_TEMPLATE.md
  10. +42 −0 .github/workflows/main.yml
  11. +21 −0 .gitignore
  12. +7 −0 .prettierignore
  13. +9 −0 .prettierrc
  14. +1 −2 LICENSE
  15. +82 −102 README.md
  16. +0 −4,380 dist/react-images.js
  17. +0 −3 dist/react-images.min.js
  18. +201 −0 docs/App/components.js
  19. +69 −0 docs/App/index.js
  20. +106 −0 docs/ImageProvider.js
  21. +19 −0 docs/ImageRoute.js
  22. +129 −0 docs/PrettyProps.js
  23. +1 −0 docs/_redirects
  24. BIN docs/favicon.ico
  25. +63 −0 docs/index.css
  26. +20 −0 docs/index.html
  27. +5 −0 docs/index.js
  28. +126 −0 docs/pages/Accessibility.js
  29. +35 −0 docs/pages/CustomComponents/AlternativeMedia/Icon.js
  30. +69 −0 docs/pages/CustomComponents/AlternativeMedia/Poster.js
  31. +43 −0 docs/pages/CustomComponents/AlternativeMedia/Progress.js
  32. +161 −0 docs/pages/CustomComponents/AlternativeMedia/View.js
  33. +28 −0 docs/pages/CustomComponents/AlternativeMedia/data.js
  34. +53 −0 docs/pages/CustomComponents/AlternativeMedia/index.js
  35. +157 −0 docs/pages/CustomComponents/ImageViewer/components.js
  36. +152 −0 docs/pages/CustomComponents/ImageViewer/index.js
  37. +144 −0 docs/pages/CustomComponents/index.js
  38. +78 −0 docs/pages/CustomStyles/CarouselExample.js
  39. +97 −0 docs/pages/CustomStyles/ModalExample.js
  40. +120 −0 docs/pages/CustomStyles/index.js
  41. +93 −0 docs/pages/Home/GalleryExample.js
  42. +97 −0 docs/pages/Home/index.js
  43. +279 −0 docs/pages/Home/props.js
  44. +13 −0 docs/pages/NoMatch.js
  45. +53 −0 docs/pages/Patterns/RouterGallery.js
  46. +28 −0 docs/pages/Patterns/index.js
  47. +33 −0 docs/pages/Thanks/data.js
  48. +66 −0 docs/pages/Thanks/index.js
  49. +147 −0 docs/pages/components.js
  50. +12 −0 docs/pages/formatters.js
  51. +7 −0 docs/pages/index.js
  52. +17 −0 docs/theme.js
  53. +4 −0 docs/utils.js
  54. +0 −5 examples/dist/.gitignore
  55. 0 examples/dist/.npmignore
  56. +0 −2,062 examples/dist/app.js
  57. +0 −4,359 examples/dist/bundle.js
  58. +0 −24,042 examples/dist/common.js
  59. +0 −264 examples/dist/example.css
  60. BIN examples/dist/favicon.ico
  61. +0 −272 examples/dist/index.html
  62. +0 −4,380 examples/dist/standalone.js
  63. +0 −5 examples/src/.gitignore
  64. 0 examples/src/.npmignore
  65. +0 −155 examples/src/app.js
  66. +0 −8 examples/src/components/DownloadButton/icon.js
  67. +0 −37 examples/src/components/DownloadButton/index.js
  68. +0 −163 examples/src/components/Gallery.js
  69. +0 −364 examples/src/example.less
  70. BIN examples/src/favicon.ico
  71. +0 −272 examples/src/index.html
  72. +0 −31 gulpfile.js
  73. +144 −0 index.d.ts
  74. +0 −428 lib/Lightbox.js
  75. +0 −111 lib/components/Arrow.js
  76. +0 −61 lib/components/Container.js
  77. +0 −95 lib/components/Footer.js
  78. +0 −89 lib/components/Header.js
  79. +0 −47 lib/components/Icon.js
  80. +0 −234 lib/components/PaginatedThumbnails.js
  81. +0 −58 lib/components/PassContext.js
  82. +0 −102 lib/components/Portal.js
  83. +0 −78 lib/components/Thumbnail.js
  84. +0 −70 lib/components/Thumbnails.js
  85. +0 −11 lib/icons/arrowLeft.js
  86. +0 −11 lib/icons/arrowRight.js
  87. +0 −11 lib/icons/close.js
  88. +0 −7 lib/icons/index.js
  89. +0 −55 lib/theme.js
  90. +0 −21 lib/utils/bindFunctions.js
  91. +0 −5 lib/utils/canUseDom.js
  92. +0 −25 lib/utils/deepMerge.js
  93. +0 −21 lib/utils/index.js
  94. +22 −0 package-scripts.js
  95. +92 −54 package.json
  96. +79 −0 rollup.config.js
  97. +0 −354 src/Lightbox.js
  98. +0 −92 src/components/Arrow.js
  99. +395 −0 src/components/Carousel.js
  100. +27 −38 src/components/Container.js
  101. +124 −74 src/components/Footer.js
  102. +138 −66 src/components/Header.js
  103. +0 −24 src/components/Icon.js
  104. +98 −0 src/components/Modal/Animation.js
  105. +22 −0 src/components/Modal/Gateway.js
  106. +214 −0 src/components/Modal/Modal.js
  107. +2 −0 src/components/Modal/index.js
  108. +98 −0 src/components/Modal/styled.js
  109. +106 −0 src/components/Navigation.js
  110. +0 −163 src/components/PaginatedThumbnails.js
  111. +0 −23 src/components/PassContext.js
  112. +43 −45 src/components/Portal.js
  113. +56 −0 src/components/Spinner.js
  114. +0 −55 src/components/Thumbnail.js
  115. +0 −45 src/components/Thumbnails.js
  116. +49 −0 src/components/View.js
  117. +16 −0 src/components/component-helpers.js
  118. +14 −0 src/components/componentBaseClassNames.js
  119. +4 −0 src/components/css-helpers.js
  120. +62 −0 src/components/defaultComponents.js
  121. +2 −0 src/components/index.js
  122. +48 −0 src/components/svg.js
  123. +92 −0 src/formatters.js
  124. +0 −5 src/icons/arrowLeft.js
  125. +0 −5 src/icons/arrowRight.js
  126. +0 −5 src/icons/close.js
  127. +0 −5 src/icons/index.js
  128. +8 −0 src/index.js
  129. +15 −0 src/index.umd.js
  130. +29 −0 src/primitives.js
  131. +77 −0 src/styles.js
  132. +0 −54 src/theme.js
  133. +16 −0 src/types.js
  134. +54 −0 src/utils.js
  135. +0 −15 src/utils/bindFunctions.js
  136. +0 −7 src/utils/canUseDom.js
  137. +0 −19 src/utils/deepMerge.js
  138. +0 −9 src/utils/index.js
  139. +57 −0 webpack.config.js
  140. +8,581 −0 yarn.lock
10 changes: 10 additions & 0 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"plugins": ["transform-class-properties", "transform-object-rest-spread"],
"presets": ["env", "react"],
"ignore": ["node_modules"],
"env": {
"test": {
"plugins": ["istanbul"]
}
}
}
14 changes: 14 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# This file is for unifying the coding style for different editors and IDEs
# editorconfig.org
root = true

[*]
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
indent_style = space
indent_size = 2

[*.md]
trim_trailing_whitespace = false
1 change: 1 addition & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
UNSPLASH_API_KEY=1271076e50e2b6d90a1f02cd440ad1e307dbefd355d87b34d2567c685db6672b
4 changes: 4 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
lib/*
dist/*
coverage/*
node_modules/*
51 changes: 47 additions & 4 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -1,7 +1,50 @@
{
"extends": "keystone",
"rules": {
"no-unused-vars": [2, {"vars": "all", "args": "after-used"}],
"quote-props": 0,
"parser": "babel-eslint",
"env": {
"browser": true,
"es6": true,
"node": true
},
"plugins": ["react"],
"settings": {
"react": {
"createClass": "createReactClass",
"pragma": "React",
"version": "16.3",
"flowVersion": "0.123"
}
},
"rules": {
"no-unused-vars": [
"error",
{
"args": "after-used",
"argsIgnorePattern": "^event$",
"ignoreRestSiblings": true,
"vars": "all",
"varsIgnorePattern": "^(glam|React)$"
}
],
"curly": [2, "multi-line"],
"jsx-quotes": 1,
"no-shadow": 1,
"no-trailing-spaces": 1,
"no-underscore-dangle": 1,
"no-unused-expressions": 1,
"object-curly-spacing": [1, "always"],
"quotes": [2, "single", "avoid-escape"],
"react/jsx-boolean-value": 1,
"react/jsx-no-undef": 1,
"react/jsx-uses-react": 1,
"react/jsx-uses-vars": 1,
"react/jsx-wrap-multilines": 1,
"react/no-did-mount-set-state": 1,
"react/no-did-update-set-state": 1,
"react/no-unknown-property": 1,
"react/react-in-jsx-scope": 1,
"react/self-closing-comp": 1,
"react/sort-prop-types": 1,
"semi": "off",
"strict": 0
}
}
11 changes: 11 additions & 0 deletions .flowconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[ignore]

[include]

[libs]

[lints]

[options]

[strict]
25 changes: 13 additions & 12 deletions .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -5,15 +5,16 @@ welcome, from issue reports to PRs and documentation / write-ups.

Before you open a PR:

* If you're planning to add or change a major feature in a PR, please ensure
the change is aligned with the project roadmap by opening an issue first,
especially if you're going to spend a lot of time on it.
* In development, run `npm start` to build (+watch) the project source, and run
the [development server](http://localhost:8000).
* Please ensure all the examples work correctly after your change. If you're
adding a major new use-case, add a new example demonstrating its use.
* Please **DO NOT** commit the build files. Make sure **ONLY** your changes to
`/src/` and `/examples/src` are included in your PR.
* Be careful to follow the code style of the project. Run `npm run lint` after
your changes and ensure you do not introduce any new errors or warnings.
* All new features and changes need documentation.
- If you're planning to add or change a major feature in a PR, please ensure
the change is aligned with the project roadmap by opening an issue first,
especially if you're going to spend a lot of time on it.
- In development, run `yarn start` to build (+watch) the project source, and run
the [development server](http://localhost:8000).
- Please ensure all the examples work correctly after your change. If you're
adding a major new use-case, add a new example demonstrating its use.
- Please **DO NOT** commit the build files. Make sure **ONLY** your changes to
`/src/` and `/examples/src` are included in your PR.
- Be careful to follow the code style of the project. Run `yarn run lint` after
your changes and ensure you do not introduce any new errors or warnings.
- All new features and changes need documentation.
- Make sure yarn.lock is updated when you add or upgrade dependencies.
169 changes: 123 additions & 46 deletions .github/HISTORY.md
Original file line number Diff line number Diff line change
@@ -1,116 +1,193 @@
# React-Images

### v1.0.0 / 2019-06-05

- v1 release

### v0.5.19 / 2018-07-19

- Fix babel lib output not transpiling

### v0.5.18 / 2018-07-19

- LightBox srcSet support for string and array #236 thanks to [lordoffreaks](https://github.com/lordoffreaks)
- Upgrade react-transition-group to v2 #238 thanks to [filipecosta01](https://github.com/filipecosta01)
- Add zindex to close button to bring it in front of spinner #225 thanks to [seanpascoe](https://github.com/seanpascoe)
- Fix #240: Spinner consumes pointer events #241 thanks to [thepatrik](https://github.com/thepatrik)
- Fix #216 Cannot read property 'complete' of undefined #217 thanks to [lkazberova](https://github.com/lkazberova)
- Fix Lightbox srcSet prop type validation #209 thanks to [kripod](https://github.com/kripod)

### v0.5.17 / 2018-02-07

- fix for "srcSet" inconsistency #204 thanks to [kripod](https://github.com/kripod) and [wmertens](https://github.com/wmertens)
- fix for preload bug when mounted with `isOpen` set to true thanks to [mkalygin](https://github.com/mkalygin)
- removed `react-spinners` dependency which was bloating the bundle, and implemented a simple loading component, thanks [kripod](https://github.com/kripod)
- support for conditional ScrollLock via new property `preventScroll` thanks to [Josh-a-e](https://github.com/Josh-a-e)

### v0.5.16 / 2018-01-30

- add preloadImage call to componentDidMount PR #200 thanks to [ilker0](https://github.com/ilker0)
- Changes some docs to clarify that yarn is the preferred package manager for this project. Removes package-lock.json. PR #191 Thanks to [jorrit](https://github.com/jorrit)
- Unmount the component tree before removing the portal node from DOM PR #180 thanks to [pleunv](https://github.com/pleunv)
- Fix react warnings by updating react-scrolllock version [jorrit](https://github.com/jorrit))

### v0.5.15 / 2018-01-25

- spinner functionality pull request #187 thanks to [mkalygin](https://github.com/mkalygin)

### v0.5.13 / 2017-11-29

- change srcset to srcSet to fix intermittent warning: Invalid DOM property `srcset`. Did you mean `srcSet`?

### v0.5.12 / 2017-11-29

- Fix React warning issue #171 thanks to [kachkaev](https://github.com/kachkaev)

### v0.5.10 / 2017-10-18

- New build

### v0.5.8 / 2017-10-06

- Incorrect peer dependency in package.json file

### v0.5.7 / 2017-10-04

- Fix warnings for React 16.0.0 and update dependencies

### v0.5.6 / 2017-09-20

- Update: Let user pass in srcSet as prop in addition to srcset thanks to [smeijer](https://github.com/smeijer)
- Fix: default arrow bg color is none
- Fix: make content div, figure, image respond to theme props #127
- Fix: caption extending and disabling backdropClosesModal click issue #156
- Fix: alignment in safari issue #105

### v0.5.5 / 2017-07-28

- Fix: let user override all possible properties with theme object thanks to [clintharris](https://github.com/clintharris)
- Fix: clicking on image closes lightbox when backdropClosesModal is set to true #152
- Fix: clicking close lightbox button fired onClose handler twice #155

### v0.5.4 / 2017-05-31

- Update: separate out prop-types for React 16 thanks to [hiyamamo](https:github.com/hiyamamo)
- Update: update react-addons-css-transition-group to react-transition-group thanks to [neptunian](https://github.com/neptunian)
- Fix: React warnings in React v15.5.0

### v0.5.2 / 2016-11-17

- Fix: keydown listeners thanks to [aknuds1](https:github.com/aknuds1) [archr](https:github.com/archr)
- Fix: thumbnail click propagation thanks to [GregoryPotdevin](https://github.com/GregoryPotdevin)
- Update: use abstracted ScrollLock component

### v0.5.1 / 2016-08-21
* Feature: Support theming with aphrodite classes
* Examples: Update options with new new prop details

- Feature: Support theming with aphrodite classes
- Examples: Update options with new new prop details

### v0.5.0 / 2016-08-20
* Feature: Added a thumbnail preview beneath the lightbox thanks to [GregoryPotdevin](https://github.com/GregoryPotdevin)
* Feature: Re-implemented the layout using `flexbox`
* Examples: Replaced local images with hot-linked Unsplash photographs

- Feature: Added a thumbnail preview beneath the lightbox thanks to [GregoryPotdevin](https://github.com/GregoryPotdevin)
- Feature: Re-implemented the layout using `flexbox`
- Examples: Replaced local images with hot-linked Unsplash photographs

---

### v0.4.11 / 2016-08-15
* Feature: Pre-load the next image based on user intention, uses new prop `preloadNextImage`
* Fix: bug with `enableKeyboardInput` where images fail to render thanks to [benhowell](https://github.com/benhowell)
* Bump dev dependencies for `react` and `react-dom` to `15.3.0`
* Increase the default max-width of the Lightbox to `1024px`

- Feature: Pre-load the next image based on user intention, uses new prop `preloadNextImage`
- Fix: bug with `enableKeyboardInput` where images fail to render thanks to [benhowell](https://github.com/benhowell)
- Bump dev dependencies for `react` and `react-dom` to `15.3.0`
- Increase the default max-width of the Lightbox to `1024px`

### v0.4.10 / 2016-08-11
* Fix react PropTypes warning. See [fixing-the-false-positive](https://facebook.github.io/react/warnings/dont-call-proptypes.html#fixing-the-false-positive-in-third-party-proptypes)
* Simplify fade transition using `react-css-addons-transition-group`

- Fix react PropTypes warning. See [fixing-the-false-positive](https://facebook.github.io/react/warnings/dont-call-proptypes.html#fixing-the-false-positive-in-third-party-proptypes)
- Simplify fade transition using `react-css-addons-transition-group`

### v0.4.9 / 2016-07-28
* Resolve react "no-unused-prop" warnings [benhowell](https://github.com/benhowell)

- Resolve react "no-unused-prop" warnings [benhowell](https://github.com/benhowell)

### v0.4.7 / 2016-07-14
* Custom controls thanks to [robintail](https://github.com/robintail)
* dependency fix for `react-addons-transition-group` thanks to [fend25](https://github.com/fend25)

- Custom controls thanks to [robintail](https://github.com/robintail)
- dependency fix for `react-addons-transition-group` thanks to [fend25](https://github.com/fend25)

### v0.4.6 / 2016-05-17
* General cleanup

- General cleanup

### v0.4.5 / 2016-05-16
* Added `imageCountSeparator` prop to replace " of " in the image count

- Added `imageCountSeparator` prop to replace " of " in the image count

### v0.4.4 / 2016-05-16
* Account for scrollbar width when opening/closing the lightbox
* Remove required flag on `onClickNext` and `onClickPrev` - may only render a single image
* Increase dialog z-index to `2001` thanks to [newsiberian](https://github.com/newsiberian)

- Account for scrollbar width when opening/closing the lightbox
- Remove required flag on `onClickNext` and `onClickPrev` - may only render a single image
- Increase dialog z-index to `2001` thanks to [newsiberian](https://github.com/newsiberian)

### v0.4.3 / 2016-05-16
* Resolve peer-dependency issues thanks to [jedwatson](https://github.com/jedwatson)

- Resolve peer-dependency issues thanks to [jedwatson](https://github.com/jedwatson)

### v0.4.2 / 2016-05-14
* Update dependencies

- Update dependencies

### v0.4.1 / 2016-05-12
* Update peer dependencies

- Update peer dependencies

### v0.4.0 / 2016-05-12
* Bump all applicable dependencies
* Remove peer dependencies

* * *
- Bump all applicable dependencies
- Remove peer dependencies

---

### v0.3.3 / 2016-05-12
* Updated website with more info + new design
* Introduction of `onClickImage` prop thanks to [pradel](https://github.com/pradel)
* Documentation for `showImageCount` thanks to [neptunian](https://github.com/neptunian)

- Updated website with more info + new design
- Introduction of `onClickImage` prop thanks to [pradel](https://github.com/pradel)
- Documentation for `showImageCount` thanks to [neptunian](https://github.com/neptunian)

### v0.3.2 / 2016-01-20
* Fix backdropClosesModal behaviour
* Update defaults: backdropClosesModal `false`, showCloseButton `true`

- Fix backdropClosesModal behaviour
- Update defaults: backdropClosesModal `false`, showCloseButton `true`

### v0.3.1 / 2016-01-15
* Updated react dependencies
* Better handling of lightbox positioning

- Updated react dependencies
- Better handling of lightbox positioning

### v0.3.0 / 2016-01-06

* Update to use ES6
* Update to use JSS
* Layout refactor:
- Moved away from CSS transforms for centering
- Improved responsiveness, performance
* Update example images to Gratisography
* Optional captions below images thanks to [@ko](https://github.com/ko)
* Optional count below images e.g. "3 of 12"
* Move close button top right of frame, and replace with × icon
- Update to use ES6
- Update to use JSS
- Layout refactor:
- Moved away from CSS transforms for centering
- Improved responsiveness, performance
- Update example images to Gratisography
- Optional captions below images thanks to [@ko](https://github.com/ko)
- Optional count below images e.g. "3 of 12"
- Move close button top right of frame, and replace with × icon

* * *
---

### v0.2.1 / 2015-11-29

* Fix bug in examples with `goto` functions
- Fix bug in examples with `goto` functions

### v0.2.0 / 2015-11-29

* Make the component stateless, now uses functions `onClickPrev`/`onClickNext`
- Make the component stateless, now uses functions `onClickPrev`/`onClickNext`

* * *
---

### v0.1.0 / 2015-11-20

* Support for `srcset` thanks to [@neptunian](https://github.com/neptunian)
- Support for `srcset` thanks to [@neptunian](https://github.com/neptunian)
4 changes: 1 addition & 3 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -6,12 +6,10 @@

**Description of changes:**


**Related issues (if any):**


**Checks:**

- [ ] Please confirm `npm run lint` ran successfully
- [ ] Please confirm `yarn run lint` ran successfully
- [ ] Please confirm that only `/src` and `/examples/src` are committed
- [ ] [if new feature] Please confirm that documentation was added to the README.md
42 changes: 42 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: CI
on: [push, pull_request]

jobs:
lint:
name: ✔️ Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: borales/actions-yarn@v2.0.0
with:
cmd: install
- uses: borales/actions-yarn@v2.0.0
with:
cmd: run lint

check-formatting:
name: ✔️ Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: borales/actions-yarn@v2.0.0
with:
cmd: install
- uses: borales/actions-yarn@v2.0.0
with:
cmd: run format:check

build:
name: 🛠️ Build
runs-on: ubuntu-latest
needs:
- lint
- check-formatting
steps:
- uses: actions/checkout@v1
- uses: borales/actions-yarn@v2.0.0
with:
cmd: install
- uses: borales/actions-yarn@v2.0.0
with:
cmd: build
21 changes: 21 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
# VS Code
.vscode

# Build
lib
dist
docs/dist
.env

# Webstorm
.idea/

@@ -8,6 +17,12 @@
logs
*.log

# Editor and other tmp files
*.swp
*.swo
*.swm
*.swn

# Runtime data
pids
*.pid
@@ -34,3 +49,9 @@ build/Release
# Dependency directory
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
node_modules

# temp files
*.swp
*.swo
*.swn
*.swm
7 changes: 7 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.cache
package-lock.json
public
font-preload-cache.json
dist
lib
node_modules
9 changes: 9 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"printWidth": 160,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "all",
"arrowParens": "avoid",
"bracketSpacing": true,
"semi": false
}
3 changes: 1 addition & 2 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
The MIT License (MIT)

Copyright (c) 2015 Joss Mackison
Copyright (c) 2018 Joss Mackison

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -19,4 +19,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

184 changes: 82 additions & 102 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,135 +1,115 @@
# React Images

[![Join the chat at https://gitter.im/react-images/Lobby](https://badges.gitter.im/react-images/Lobby.svg)](https://gitter.im/react-images/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
### ⚠️ Warning!

A simple, responsive lightbox component for displaying an array of images.
**Don't use this in a new project.** This package hasn't been properly maintained in a long time and there are much better options available.

**Instead, try...**

### Quick start
- [React Responsive Carousel](http://react-responsive-carousel.js.org/)

---

A mobile-friendly, highly customizable, carousel component for displaying media in ReactJS.

### Browser support

Should work in every major browser... maybe even IE10 and IE11?

### Getting Started

Start by installing `react-images`

```bash
npm install --save react-images
npm install react-images
```

```jsx
import React from 'react';
import Lightbox from 'react-images';
or

export default class Sample extends React.Component {
...
render() {
return (
<Lightbox
images={[{ src: 'http://example.com/img1.jpg' }, { src: 'http://example.com/img2.jpg' }]}
isOpen={this.state.lightboxIsOpen}
onClickPrev={this.gotoPrevious}
onClickNext={this.gotoNext}
onClose={this.closeLightbox}
/>
);
}
}
```bash
yarn add react-images
```

**If you were using `0.x` versions:** library was significantly rewritten for `1.x` version and contains several breaking changes.
The best way to upgrade is to read the docs and follow the examples.

## Demo & Examples
Please note that the default footer parses HTML automatically (such as `<b>I'm bold!</b>`) but it **does not implement any form of XSS or sanitisation**. You should do that yourself before passing it into the caption field of react-images.

Live demo: [jossmac.github.io/react-images](http://jossmac.github.io/react-images/)
### Using the Carousel

To build the examples locally, run:
Import the carousel from `react-images` at the top of a
component and then use it in the render function.

```
npm install
npm start
```jsx
import React from 'react'
import Carousel from 'react-images'

const images = [{ source: 'path/to/image-1.jpg' }, { source: 'path/to/image-2.jpg' }]

class Component extends React.Component {
render() {
return <Carousel views={images} />
}
}
```

Then open [`localhost:8000`](http://localhost:8000) in a browser.
### Using the Modal

### Using srcset
Import the modal and optionally the modal gateway from
`react-images` at the top of a component and then use it in
the render function.

The `ModalGateway` will insert the modal just before the
end of your `<body />` tag.

Example using srcset:
```jsx
<Lightbox
images={LIGHTBOX_IMAGE_SET}
...
/>

const LIGHTBOX_IMAGE_SET = [
{
src: 'http://example.com/example/img1.jpg',
srcset: [
'http://example.com/example/img1_1024.jpg 1024w',
'http://example.com/example/img1_800.jpg 800w',
'http://example.com/example/img1_500.jpg 500w',
'http://example.com/example/img1_320.jpg 320w',
],
},
{
src: 'http://example.com/example/img2.jpg',
srcset: [
'http://example.com/example/img2_1024.jpg 1024w',
'http://example.com/example/img2_800.jpg 800w',
'http://example.com/example/img2_500.jpg 500w',
'http://example.com/example/img2_320.jpg 320w',
],
}
];
import React from 'react'
import Carousel, { Modal, ModalGateway } from 'react-images'

```
const images = [{ source: 'path/to/image-1.jpg' }, { source: 'path/to/image-2.jpg' }]

Notes on srcset support:
class Component extends React.Component {
state = { modalIsOpen: false }
toggleModal = () => {
this.setState(state => ({ modalIsOpen: !state.modalIsOpen }))
}
render() {
const { modalIsOpen } = this.state

The srcset attribute is supported by some modern browsers. Results of browser implementation and behaviour may vary. The sizes attribute uses the default maxWidth CSS property set to the image. By default this is 80% so 80vw.
return (
<ModalGateway>
{modalIsOpen ? (
<Modal onClose={this.toggleModal}>
<Carousel views={images} />
</Modal>
) : null}
</ModalGateway>
)
}
}
```

Another thing to note is that 'h' or height in the srcset attribute does not yet exist. Because of the nature of the fixed height of a Lightbox this is problematic for portrait sized images. You will need to calculate what the best 'w' size for a portrait size ought to be given the height of the fixed viewport otherwise unnecessarily large images will be fetched. See issue: [https://github.com/ResponsiveImagesCG/picture-element/issues/86](https://github.com/ResponsiveImagesCG/picture-element/issues/86)
### Advanced Image Lists

Read more about the srcset and sizes attributes here: [https://ericportis.com/posts/2014/srcset-sizes/](https://ericportis.com/posts/2014/srcset-sizes/).
The simplest way to define a list of images for the carousel looks like:

### Adding Captions
```jsx
const images = [{ source: 'path/to/image-1.jpg' }, { source: 'path/to/image-2.jpg' }]
```

Example using caption for the first image:
However, react-images supports several other properties on each image object than just `source`. For example:

```jsx
<Lightbox
images={LIGHTBOX_IMAGE_SET}
...
/>

const LIGHTBOX_IMAGE_SET = [
{
src: 'http://example.com/example/img1.jpg',
caption: 'Sydney, Australia - Photo by Jill Smith',
},
{
src: 'http://example.com/example/img2.jpg',
}
];

const image = {
caption: "An image caption as a string, React Node, or a rendered HTML string",
alt: "A plain string to serve as the image's alt tag",
source: {
download: "A URL to serve a perfect quality image download from",
fullscreen: "A URL to load a very high quality image from",
regular: "A URL to load a high quality image from",
thumbnail: "A URL to load a low quality image from"
};
}
```

Note that the caption is an entirely optional property, as can be seen in the first gallery on the [example page](http://jossmac.github.io/react-images/). The first image has a single line caption, the second demonstrates multiline, and the remaining images are without captions, entirely.

## Options

Property | Type | Default | Description
:-----------------------|:--------------|:--------------|:--------------------------------
backdropClosesModal | bool | false | Allow users to exit the lightbox by clicking the backdrop
closeButtonTitle | string | ' Close (Esc) ' | Customize close esc title
enableKeyboardInput | bool | true | Supports keyboard input - <code>esc</code>, <code>arrow left</code>, and <code>arrow right</code>
currentImage | number | 0 | The index of the image to display initially
customControls | array | undefined | An array of elements to display as custom controls on the top of lightbox
images | array | undefined | Required. An array of objects containing valid src and srcset values of img element
imageCountSeparator | String | ' of ' | Customize separator in the image count
isOpen | bool | false | Whether or not the lightbox is displayed
leftArrowTitle | string | ' Previous (Left arrow key) ' | Customize of left arrow title
onClickPrev | func | undefined | Fired on request of the previous image
onClickNext | func | undefined | Fired on request of the next image
onClose | func | undefined | Required. Handle closing of the lightbox
onClickImage | func | undefined | Handle click on image
onClickThumbnail | func | undefined | Handle click on thumbnail
preloadNextImage | bool | true | Based on the direction the user is navigating, preload the next available image
rightArrowTitle | string | ' Next (Right arrow key) ' | Customize right arrow title
showCloseButton | bool | true | Optionally display a close "X" button in top right corner
showImageCount | bool | true | Optionally display image index, e.g., "3 of 20"
width | number | 1024 | Maximum width of the carousel; defaults to 1024px
All these fields are optional except `source`. Additionally, if using an object of URLs (rather than a plain string URL) as your `source`, you must specify the `regular` quality URL.
4,380 changes: 0 additions & 4,380 deletions dist/react-images.js

This file was deleted.

3 changes: 0 additions & 3 deletions dist/react-images.min.js

This file was deleted.

201 changes: 201 additions & 0 deletions docs/App/components.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
// @flow
// @jsx glam

import glam from 'glam'
import React, { Component, type Node } from 'react'
import { Link, withRouter } from 'react-router-dom'
import { colors } from '../theme'
import { smallDevice, largeDevice } from '../utils'

const navWidth = 180
const appWidth = 840
const appGutter = 15
const contentGutter = 20
const pagePadding = 120

export const AppContainer = (props: any) => (
<div
css={{
boxSizing: 'border-box',
marginLeft: 'auto',
marginRight: 'auto',
maxWidth: appWidth,
minHeight: '100vh',
padding: `0 ${appGutter}px ${pagePadding}px`,
}}
{...props}
/>
)
export const PageContent = (props: any) => (
<div
css={{
marginLeft: 'auto',
marginRight: 'auto',
paddingBottom: contentGutter,
paddingTop: contentGutter,

[smallDevice]: {
paddingTop: 70,
},
}}
{...props}
/>
)
export const AppContent = (props: any) => (
<div
css={{
flex: '1 1 auto',
marginLeft: 'auto',
marginRight: 'auto',

[largeDevice]: {
paddingLeft: navWidth,
},
}}
{...props}
/>
)
export const Nav = ({ children }: { children: Node }) => (
<nav
css={{
position: 'fixed',
zIndex: 1,

[smallDevice]: {
alignItems: 'center',
backgroundColor: 'rgba(255, 255, 255, 0.96)',
boxShadow: 'inset 0 -1px 0 rgba(0, 0, 0, 0.1)',
display: 'flex ',
fontSize: 13,
marginLeft: -appGutter,
marginRight: -appGutter,
overflowX: 'auto',
top: 0,
width: '100%',
WebkitOverflowScrolling: 'touch',
},

[largeDevice]: {
display: 'block',
float: 'left',
paddingTop: contentGutter,
width: navWidth,
},
}}
>
{children}
<Footer />
</nav>
)

type ItemProps = {
children: Node,
icon: string,
isSelected: boolean,
to: string,
}
export const NavItem = ({ children, icon, isSelected, to }: ItemProps) => {
const attrs = isSelected ? { 'aria-current': 'page' } : {}
return (
<Link
{...attrs}
to={to}
css={{
alignItems: 'center',
border: 0,
color: isSelected ? colors.N100 : colors.N60,
fontWeight: isSelected ? 500 : null,
position: 'relative',
textDecoration: 'none',
whiteSpace: 'nowrap',
verticalAlign: 'middle',

':hover, :active': {
color: colors.N80,
textDecoration: 'none',
},

[smallDevice]: {
boxShadow: isSelected ? 'inset 0 -2px 0 black' : null,
display: 'inline-flex ',
padding: `8px ${appGutter}px`,
},

[largeDevice]: {
backgroundColor: isSelected ? 'white' : 'transparent',
display: 'flex ',
padding: '8px 20px 8px 0',
},
}}
>
<span css={{ fontSize: '1.33em', marginRight: '0.5em' }} role="presentation">
{icon}
</span>
{children}
</Link>
)
}

const Footer = () => {
const size = window.innerWidth > 769 ? 'large' : null

return (
<div
css={{
[smallDevice]: {
paddingLeft: appGutter,
paddingRight: appGutter,
},
[largeDevice]: {
position: 'fixed',
bottom: 30,
},
}}
>
<a
className="github-button"
href="https://github.com/jossmac/react-images"
data-size={size}
data-show-count="true"
aria-label="Star jossmac/react-images on GitHub"
>
Star
</a>
<p
css={{
color: colors.N40,
fontSize: '0.85em',
marginBottom: 0,
[smallDevice]: { display: 'none' },
}}
>
with ❤️ by{' '}
<a href="https://twitter.com/jossmackison" target="_blank">
jossmac
</a>
</p>
</div>
)
}

// Return scroll to top on route change
class ScrollToTop extends Component<*> {
componentDidUpdate(prevProps) {
const { history, location } = this.props

// do not influence scroll on browser back/forward
if (history.action === 'POP') return

// no scroll when extending the current path
const pathArr = location.pathname.split('/')
if (!prevProps.location.pathname.includes(pathArr[1])) {
window.scrollTo(0, 0)
}
}

render() {
return this.props.children
}
}

export const ScrollRestoration = withRouter(ScrollToTop)
69 changes: 69 additions & 0 deletions docs/App/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// @flow
// @jsx glam

import glam from 'glam'
import React, { Component } from 'react'
import { HashRouter, Route, Switch } from 'react-router-dom'
import { Helmet } from 'react-helmet'

import { Accessibility, CustomComponents, CustomStyles, Home, NoMatch, Patterns, Thanks } from '../pages'
import ImageRoute from '../ImageRoute'
import withImages, { type ProviderProps } from '../ImageProvider'
import { AppContainer, PageContent, AppContent, Nav, NavItem, ScrollRestoration } from './components'

const links = [
{ icon: '🌄', label: 'Intro', path: '/' },
{ icon: '🏗', label: 'Components', path: '/components' },
{ icon: '🎨', label: 'Styles', path: '/styles' },
{ icon: '💖', label: 'Accessibility', path: '/accessibility' },
{ icon: '🤖', label: 'Patterns', path: '/patterns' },
{ icon: '🎉', label: 'Thanks', path: '/thanks' },
]

class App extends Component<*> {
routeProps: ProviderProps
render() {
const routeProps = (this.routeProps = this.props)
return (
<HashRouter>
<ScrollRestoration>
<AppContainer>
<Route
render={({ location }) => (
<Nav>
{links.map(l => {
const isSelected = l.path.length > 1 ? location.pathname.includes(l.path) : location.pathname === l.path
return (
<NavItem icon={l.icon} key={l.path} isSelected={isSelected} to={l.path}>
{l.label}
</NavItem>
)
})}
</Nav>
)}
/>
<AppContent>
<Helmet>
<title>React Images</title>
<meta name="description" content="A mobile-friendly, highly customizable, carousel component for displaying media in ReactJS" />
</Helmet>
<PageContent>
<Switch>
<ImageRoute exact path="/" component={Home} {...routeProps} />
<ImageRoute exact path="/styles" component={CustomStyles} {...routeProps} />
<ImageRoute exact path="/components" component={CustomComponents} {...routeProps} />
<ImageRoute exact path="/accessibility" component={Accessibility} {...routeProps} />
<ImageRoute path="/patterns/:currentIndex?" component={Patterns} {...routeProps} />
<Route component={Thanks} />
<Route component={NoMatch} />
</Switch>
</PageContent>
</AppContent>
</AppContainer>
</ScrollRestoration>
</HashRouter>
)
}
}

export default withImages(App)
106 changes: 106 additions & 0 deletions docs/ImageProvider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// @flow

import React, { Component, type ComponentType, type Node } from 'react'

type Source =
| string
| {
download?: string,
fullscreen?: string,
regular: string,
thumbnail?: string,
}
type Author =
| string
| {
avatar: string,
name: string,
url: string,
}
type Images = Array<{
caption?: string | Node,
author?: Author,
source: Source,
}>
export type ProviderProps = {
images: Images,
isLoading: boolean,
}

function transformPaths({ links, urls }) {
return {
download: links.download_location,
fullscreen: urls.full,
regular: urls.regular,
thumbnail: urls.thumb,
}
}
function getReferrerLink(username) {
const id = 'react-images'
return `https://unsplash.com/${username}?utm_source=${id}&utm_medium=referral`
}
function transformImageData(arr) {
return arr.map(img => ({
author: {
avatar: img.user.profile_image.medium,
name: img.user.name,
url: getReferrerLink(img.user.username),
},
color: img.color,
caption: img.description,
createdAt: img.created_at,
likes: img.likes,
source: transformPaths(img),
title: img.title,
}))
}
function getApiUrl() {
const query = 'wildlife,animal'
const url = 'https://api.unsplash.com/search/photos/?page=1&per_page=12&query='

// $FlowFixMe: escape global `process.env.UNSPLASH_API_KEY`
return `${url}${query}&client_id=${process.env.UNSPLASH_API_KEY}`
}

const dataKey = 'react_images_docs'

function getData() {
return JSON.parse(window.sessionStorage.getItem(dataKey))
}
function setData(data) {
window.sessionStorage.setItem(dataKey, JSON.stringify(data))

return data
}

export default function withImages(WrappedComponent: ComponentType<*>) {
return class ImageProvider extends Component<{}, ProviderProps> {
state = { images: [], isLoading: true }
componentDidMount() {
// using local storage to prevent API requests, don't want to exceed
// the applications Unsplash limit
const storedData = getData()

// bail if images already available
if (storedData) {
this.setState({ images: storedData, isLoading: false })
return
}

// set state to force re-render on the route
fetch(getApiUrl())
.then(res => res.json())
.then(data => {
console.log('data.results', data.results)
const images = setData(transformImageData(data.results))
this.setState({ images, isLoading: false })
})
.catch(err => {
console.error('Error occured when fetching images', err)
})
}
render() {
return <WrappedComponent {...this.props} {...this.state} />
}
}
}
19 changes: 19 additions & 0 deletions docs/ImageRoute.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// @flow

import React, { type ComponentType } from 'react'
import { Route } from 'react-router-dom'
import { type ProviderProps } from './ImageProvider'

type Props = ProviderProps & {
component: ComponentType<*>,
exact?: boolean,
path: string,
}

const ImageRoute = (props: Props) => {
const { component: Component, images, isLoading, ...rest } = props

return <Route {...rest} children={routeProps => <Component images={images} isLoading={isLoading} {...routeProps} />} />
}

export default ImageRoute
129 changes: 129 additions & 0 deletions docs/PrettyProps.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
// @flow
// @jsx glam

import glam from 'glam'
import React, { type Node } from 'react'

import SyntaxHighlighter, { registerLanguage } from 'react-syntax-highlighter/prism-light'
import typescript from 'react-syntax-highlighter/languages/prism/typescript'
import { coy } from 'react-syntax-highlighter/styles/prism'

import { colors } from './theme'

registerLanguage('typescript', typescript)

type Props = {
defaultValue: any,
description: any,
isRequired: boolean,
name: string,
type: string,
typeDefinition: string,
}

const TypeDefinition = ({ children }: { children: Node }) => {
return (
<SyntaxHighlighter
language="typescript"
style={coy}
customStyle={{
backgroundColor: 'transparent',
borderRadius: 4,
fontSize: 12,
marginTop: '2em',
maxWidth: '100%',
overflowX: 'auto',
WebkitOverflowScrolling: 'touch',
}}
>
{children}
</SyntaxHighlighter>
)
}

const Heading = props => (
<h4
css={{
fontSize: '1rem',
fontWeight: 'normal',
lineHeight: 1.4,
margin: '2em 0 0',
}}
{...props}
/>
)

const HeadingDefault = props => (
<code
css={{
color: colors.N60,
}}
{...props}
/>
)

const HeadingRequired = props => (
<span
css={{
color: colors.red,
}}
{...props}
/>
)

const code = {
borderRadius: 3,
color: colors.N100,
fontFamily: 'Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace',
margin: 0,
padding: '0.2em 0.4em',
whiteSpace: 'nowrap',
}

const HeadingType = props => (
<span
css={{
...code,
backgroundColor: 'rgba(251, 132, 191, 0.14)',
}}
{...props}
/>
)

const HeadingName = props => (
<span
css={{
...code,
backgroundColor: 'rgba(0, 215, 255, 0.1)',
marginRight: '0.5em',
}}
{...props}
/>
)

const PrettyProps = (props: Props) => {
let typeName = props.type
if (typeName === 'union') {
typeName = 'union'
}

const defaultValue = props.defaultValue ? props.defaultValue : null
const description = typeof props.description === 'string' ? <p>{props.description}</p> : props.description

return (
<div>
<Heading>
<code>
<HeadingName>{props.name}</HeadingName>
<HeadingType>{typeName}</HeadingType>
{defaultValue && <HeadingDefault> = {defaultValue}</HeadingDefault>}
{props.isRequired ? <HeadingRequired> required</HeadingRequired> : null}
</code>
</Heading>
{description}
{props.typeDefinition ? <TypeDefinition>{props.typeDefinition}</TypeDefinition> : null}
</div>
)
}

export default PrettyProps
1 change: 1 addition & 0 deletions docs/_redirects
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/* /index.html 200
Binary file added docs/favicon.ico
Binary file not shown.
63 changes: 63 additions & 0 deletions docs/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
body {
-moz-font-feature-settings: 'liga' on;
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
color: #253858;
font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', Helvetica,
sans-serif;
font-style: normal;
font-weight: 400;
margin: 0;
padding: 0;
text-rendering: optimizeLegibility;
}

/* explicit anchor styles: don't interfere with Carousel or Modals styles */
h1 a,
h2 a,
h3 a,
h4 a,
h5 a,
h6 a,
ul a,
ol a,
p a {
border-bottom: 1px solid rgba(0, 215, 255, 0.25);
color: #00d7ff;
text-decoration: none;
}
a:hover {
border-bottom-color: rgba(0, 215, 255, 0.66);
text-decoration: none;
}

p,
ul,
ol {
line-height: 1.5;
}

h1,
h2,
h3,
h4,
h5 {
color: #091e42;
}
h1 {
margin-top: 0;
}
h2,
h3 {
margin-top: 1.66em;
}
h6 {
color: #777;
margin-bottom: 0.25em;
text-transform: uppercase;
}

code {
font-family: SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono',
'Courier New', monospace;
}
20 changes: 20 additions & 0 deletions docs/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<!doctype html>
<head>
<meta charset="utf-8">
<title>React-Images</title>
<meta name="viewport" content="initial-scale=1.0,user-scalable=no,maximum-scale=1,width=device-width">
<meta name="keywords" content="react,reactjs,react component, images, carousel, lightbox, gallery, modal">
<meta property="og:locale" content="en-us">
<meta property="og:title" content="React-Images">
<meta property="og:description" content="A mobile-friendly, highly customizable, carousel component for displaying media in ReactJS">
<meta property="og:url" content="https://jossmac.github.io/react-images">
<meta property="og:site_name" content="React-Images">
<meta property="og:type" content="article">
<link rel="stylesheet" type="text/css" href="index.css">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<div id="root"></div>
<script async defer src="https://buttons.github.io/buttons.js"></script>
<script src="index.js"></script>
</body>
5 changes: 5 additions & 0 deletions docs/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'

ReactDOM.render(<App />, document.getElementById('root'))
126 changes: 126 additions & 0 deletions docs/pages/Accessibility.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
// @flow
// @jsx glam
import glam from 'glam'
import React, { Component } from 'react'
import { Helmet } from 'react-helmet'

import Carousel, { Modal, ModalGateway } from '../../src/components'
import { type ProviderProps } from '../ImageProvider'

import { Code, Title } from './components'

const Table = props => (
<table
css={{
borderCollapse: 'collapse',
borderSpacing: 0,
border: 0,
}}
{...props}
/>
)
const Cell = props => <td css={{ padding: 4 }} {...props} />

type State = {
currentView?: number,
lightboxIsOpen: boolean,
}

export default class Accessibility extends Component<ProviderProps, State> {
state = {
currentView: undefined,
lightboxIsOpen: false,
}
toggleLightbox = (currentView: number) => {
this.setState(state => ({
lightboxIsOpen: !state.lightboxIsOpen,
currentView,
}))
}
render() {
const { images, isLoading } = this.props
const { currentView, lightboxIsOpen } = this.state

return (
<div>
<Helmet>
<title>Accessibility - React Images</title>
<meta
name="description"
content="React-Images comes with accessible features out-of-the box. Keyboard
support, roles, and aria-attribution on the applicable elements."
/>
</Helmet>
<Title>Accessibility</Title>
<p>React-Images comes with accessible features out-of-the box. Keyboard support, roles, and aria-attribution on the applicable elements.</p>

<h2>Keyboard Support</h2>
<h3>Carousel</h3>
<Table>
<tbody>
<tr>
<Cell width={60}>
<Code>Left</Code>
</Cell>
<Cell>move to the previous view</Cell>
</tr>
<tr>
<Cell width={60}>
<Code>Right</Code>
</Cell>
<Cell>move to the next view</Cell>
</tr>
<tr>
<Cell width={60}>
<Code>Home</Code>
</Cell>
<Cell>move to the first view</Cell>
</tr>
<tr>
<Cell width={60}>
<Code>End</Code>
</Cell>
<Cell>move to the last view</Cell>
</tr>
<tr>
<Cell width={60}>
<Code>1-9</Code>
</Cell>
<Cell>number keys navigate to their respective view</Cell>
</tr>
</tbody>
</Table>

<h3>Modal</h3>
<Table>
<tbody>
<tr>
<Cell width={60}>
<Code>Esc</Code>
</Cell>
<Cell>closes the modal</Cell>
</tr>
<tr>
<Cell width={60}>
<Code>F</Code>
</Cell>
<Cell>toggles full screen</Cell>
</tr>
</tbody>
</Table>
{/* <p>
// move to the previous view case 'ArrowUp': // move to the next view
case 'ArrowDown': // move to first view case 'Home': // move to last
view case 'End': // 1 - 9 keys mapped to respective slide number
</p> */}
<ModalGateway>
{lightboxIsOpen && !isLoading ? (
<Modal onClose={this.toggleLightbox}>
<Carousel trackProps={{ currentView }} views={images} />
</Modal>
) : null}
</ModalGateway>
</div>
)
}
}
35 changes: 35 additions & 0 deletions docs/pages/CustomComponents/AlternativeMedia/Icon.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// @flow
// @jsx glam
import glam from 'glam'
import React from 'react'

type IconProps = { size: number, type: 'play' | 'pause' }
const Icon = ({ size = 64, type }: IconProps) => {
const get = {
play: (
<path d="M12 20.016c4.406 0 8.016-3.609 8.016-8.016s-3.609-8.016-8.016-8.016-8.016 3.609-8.016 8.016 3.609 8.016 8.016 8.016zM12 2.016c5.531 0 9.984 4.453 9.984 9.984s-4.453 9.984-9.984 9.984-9.984-4.453-9.984-9.984 4.453-9.984 9.984-9.984zM9.984 16.5v-9l6 4.5z" />
),
pause: (
<path d="M12.984 15.984v-7.969h2.016v7.969h-2.016zM12 20.016c4.406 0 8.016-3.609 8.016-8.016s-3.609-8.016-8.016-8.016-8.016 3.609-8.016 8.016 3.609 8.016 8.016 8.016zM12 2.016c5.531 0 9.984 4.453 9.984 9.984s-4.453 9.984-9.984 9.984-9.984-4.453-9.984-9.984 4.453-9.984 9.984-9.984zM9 15.984v-7.969h2.016v7.969h-2.016z" />
),
}

return (
<svg
role="presentation"
viewBox="0 0 24 24"
css={{
display: 'inline-block',
fill: 'currentColor',
height: size,
stroke: 'currentColor',
strokeWidth: 0,
width: size,
}}
>
{get[type]}
</svg>
)
}

export default Icon
69 changes: 69 additions & 0 deletions docs/pages/CustomComponents/AlternativeMedia/Poster.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// @flow
// @jsx glam
import glam from 'glam'
import React from 'react'

import Icon from './Icon'
import type { ViewShape } from './View'

type Props = { data: ViewShape, onClick: any => void }

const ratio = `${((9 / 16) * 100) / 2}%`
const gutter = 2

export const Posters = (props: any) => (
<div
css={{
marginLeft: -gutter,
marginRight: -gutter,
overflow: 'hidden',
}}
{...props}
/>
)

export const Poster = ({ data, onClick }: Props) => (
<div
role="img"
css={{
backgroundColor: '#eee',
backgroundImage: `url(${data.poster})`,
backgroundRepeat: 'no-repeat',
backgroundSize: 'cover',
boxSizing: 'border-box',
float: 'left',
lineHeight: 0,
margin: gutter,
overflow: 'hidden',
paddingBottom: ratio,
position: 'relative',
width: `calc(50% - ${gutter * 2}px)`,
}}
>
<button
onClick={onClick}
type="button"
css={{
background: 0,
border: 0,
color: 'white',
cursor: 'pointer',
padding: 0,
height: 64,
left: '50%',
marginLeft: -32,
marginTop: -32,
opacity: 0.66,
outline: 0,
position: 'absolute',
top: '50%',
transition: 'opacity 200ms',
width: 64,

':hover': { opacity: 1 },
}}
>
<Icon type="play" />
</button>
</div>
)
43 changes: 43 additions & 0 deletions docs/pages/CustomComponents/AlternativeMedia/Progress.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// @flow
// @jsx glam
import glam from 'glam'
import React from 'react'
import { colors } from '../../../theme'

type Props = { progress: number }

const height = 6

const ProgressBar = ({ progress }: Props) => (
<div
css={{
backgroundColor: 'rgba(255, 255, 255, 0.2)',
borderRadius: height / 2,
flex: 1,
height: height,
overflow: 'hidden',
position: 'relative',
}}
>
<Progress progress={progress} />
</div>
)

const Progress = ({ progress }: Props) => (
<div
css={{
backgroundColor: colors.primary,
borderRadius: height / 2,
top: 0,
left: 0,
bottom: 0,
position: 'absolute',
transition: 'width 333ms',
}}
style={{
width: `${progress}%`,
}}
/>
)

export default ProgressBar
161 changes: 161 additions & 0 deletions docs/pages/CustomComponents/AlternativeMedia/View.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
// @flow
// @jsx glam
import glam from 'glam'
import React, { Component, type ElementRef } from 'react'
import rafScheduler from 'raf-schd'

import Icon from './Icon'
import ProgressBar from './Progress'
import { colors } from '../../../theme'

type UrlShape = {
type: 'video/mp4' | 'video/ogg',
src: string,
}

export type ViewShape = {
poster: string,
urls: Array<UrlShape>,
}

type ViewProps = {
currentIndex: number,
data: ViewShape,
interactionIsIdle: boolean,
}
type ViewState = { paused: boolean, progress: number }

function calculateProgress({ currentTime, duration }) {
return (100 / duration) * currentTime
}

const Footer = ({ interactionIsIdle, ...props }) => (
<div
css={{
alignItems: 'center',
bottom: 0,
display: 'flex ',
left: 0,
opacity: interactionIsIdle ? 0 : 1,
padding: 10,
paddingRight: 15,
position: 'absolute',
right: 0,
transition: 'opacity 300ms',
}}
style={{
background: 'linear-gradient(rgba(0,0,0,0), rgba(0,0,0,0.44))',
}}
{...props}
/>
)
const Button = props => (
<button
type="button"
css={{
background: 0,
border: 0,
color: 'white',
cursor: 'pointer',
height: 32,
marginRight: 10,
outline: 0,
padding: 0,
opacity: 0.66,
width: 32,

':hover': { opacity: 1 },
':active': { color: colors.primary },
}}
{...props}
/>
)

export default class View extends Component<ViewProps, ViewState> {
video: HTMLVideoElement
state = { paused: true, progress: 0 }
componentDidMount() {
this.video.addEventListener('play', this.handlePlay, false)
this.video.addEventListener('pause', this.handlePause, false)
this.video.addEventListener('timeupdate', this.handleTimeUpdate, false)
}
componentWillUnmount() {
this.video.removeEventListener('play', this.handlePlay)
this.video.removeEventListener('pause', this.handlePause)
this.video.removeEventListener('timeupdate', this.handleTimeUpdate)
}
componentDidUpdate(prevProps: ViewProps) {
if (this.props.currentIndex !== prevProps.currentIndex) {
this.playOrPause('pause')
}
}
handlePlay = () => {
this.setState({ paused: false })
}
handleTimeUpdate = rafScheduler(() => {
const progress = calculateProgress({
currentTime: this.video.currentTime,
duration: this.video.duration,
})

this.setState({ progress })
})
handlePause = () => {
this.setState({ paused: true })
}
playOrPause = (type: 'play' | 'pause' | 'toggle' = 'toggle') => {
const { video } = this

switch (type) {
case 'play':
video.play()
break
case 'pause':
video.pause()
break
default:
if (video.paused || video.ended) {
video.play()
} else {
video.pause()
}
}
}
getVideo = (ref: ElementRef<*>) => {
this.video = ref
}
render() {
const { data, interactionIsIdle } = this.props
const { progress } = this.state
const width = 854

return (
<div
css={{
backgroundColor: 'black',
lineHeight: 0,
marginLeft: 'auto',
marginRight: 'auto',
maxWidth: width,
position: 'relative',
textAlign: 'center',
}}
>
<video autoPlay={false} controls={false} onClick={this.playOrPause} poster={data.poster} ref={this.getVideo} style={{ width: '100%', height: 'auto' }}>
{data.sources.map((vid, idx) => (
<source key={idx} src={vid.url} type={vid.type} />
))}
Your browser does not support HTML5 video.
</video>
{this.video ? (
<Footer interactionIsIdle={interactionIsIdle}>
<Button onClick={this.playOrPause}>
<Icon type={this.state.paused ? 'play' : 'pause'} size={32} />
</Button>
<ProgressBar progress={progress} />
</Footer>
) : null}
</div>
)
}
}
28 changes: 28 additions & 0 deletions docs/pages/CustomComponents/AlternativeMedia/data.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
export const videos = [
{
poster: 'https://peach.blender.org/wp-content/uploads/bbb-splash.png',
sources: [
{
type: 'video/mp4',
url: 'https://download.blender.org/peach/trailer/trailer_480p.mov',
},
{
type: 'video/ogg',
url: 'https://download.blender.org/peach/trailer/trailer_400p.ogg',
},
],
},
{
poster: 'https://durian.blender.org/wp-content/uploads/2010/06/02.b_comp_000296.jpg',
sources: [
{
type: 'video/mp4',
url: 'https://download.blender.org/durian/trailer/sintel_trailer-480p.mp4',
},
{
type: 'video/ogg',
url: 'https://download.blender.org/durian/trailer/sintel_trailer-480p.ogv',
},
],
},
]
53 changes: 53 additions & 0 deletions docs/pages/CustomComponents/AlternativeMedia/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// @flow

import React, { Component } from 'react'
import Carousel, { Modal, ModalGateway } from '../../../../src/components'

import { videos } from './data'
import { Poster, Posters } from './Poster'
import View from './View'
import { Code, Heading } from '../../components'

type Props = {}
type State = { currentModal: number | null }
export default class AlternativeMedia extends Component<Props, State> {
state = { currentModal: null }
toggleModal = (index: number | null = null) => {
this.setState({ currentModal: index })
}
render() {
const { currentModal } = this.state

return (
<div>
<Heading source="/CustomComponents/AlternativeMedia/index.js">Alternative Media</Heading>
<p>
In this example the data passed to <Code>views</Code> contains source and poster information. The <Code>&lt;View /&gt;</Code> component has been
replaced to render an HTML5 video tag and custom controls.
</p>
<p>
Videos courtesy of{' '}
<a href="https://peach.blender.org/" target="_blank">
"Big Buck Bunny"
</a>{' '}
and{' '}
<a href="https://durian.blender.org/" target="_blank">
"Sintel"
</a>
</p>
<Posters>
{videos.map((vid, idx) => (
<Poster key={idx} data={vid} onClick={() => this.toggleModal(idx)} />
))}
</Posters>
<ModalGateway>
{Number.isInteger(currentModal) ? (
<Modal allowFullscreen={false} closeOnBackdropClick={false} onClose={this.toggleModal}>
<Carousel currentIndex={currentModal} components={{ Footer: null, View }} views={videos} />
</Modal>
) : null}
</ModalGateway>
</div>
)
}
}
157 changes: 157 additions & 0 deletions docs/pages/CustomComponents/ImageViewer/components.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
// @flow
// @jsx glam

import glam from 'glam'
import React from 'react'
import { colors } from '../../../theme'
import { smallDevice, largeDevice } from '../../../utils'

export const Header = ({ currentView, modalProps }) => {
const { author, caption, createdAt, likes } = currentView
const { onClose } = modalProps

const createdDate = new Date(createdAt).toLocaleDateString()

return (
<div
css={{
alignItems: 'center',
backgroundColor: 'white',
boxShadow: '0 1px 0 rgba(0, 0, 0, 0.1)',
color: colors.N80,
display: 'flex ',
flex: '0 0 auto',
height: 54,
justifyContent: 'space-between',

[smallDevice]: {
paddingLeft: 10,
paddingRight: 10,
},
[largeDevice]: {
paddingLeft: 20,
paddingRight: 20,
},
}}
>
<div css={{ alignItems: 'center', display: 'flex ', minWidth: 0 }}>
<img
css={{
borderRadius: 3,
flexShrink: 0,
height: 32,
marginRight: 8,
width: 32,
}}
src={author.avatar}
/>
<div css={{ fontSize: '0.85em', minWidth: 0 }}>
<div css={{ color: colors.N100, fontWeight: 500 }}>{author.name}</div>
<div
css={{
color: colors.N60,
marginTop: '0.25em',
minWidth: 0,
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
}}
>
<span>{createdDate}</span>
{caption ? <span> &mdash; {caption}</span> : null}
</div>
</div>
</div>
<div css={{ alignItems: 'center', display: 'flex ' }}>
<Button
onClick={() => {
console.log('Like clicked!')
}}
>
<span
css={{
backgroundColor: 'white',
borderRadius: 8,
display: 'inline-block',
fontSize: '0.7em',
fontWeight: 500,
lineHeight: 1,
marginRight: -12,
marginTop: 8,
padding: '1px 4px',
position: 'relative',
}}
>
{likes}
</span>
<Heart />
</Button>
<Button
onClick={onClose}
css={{
borderLeft: `1px solid ${colors.N10}`,
paddingLeft: 10,
[largeDevice]: { marginRight: -10 },
}}
>
<Close />
</Button>
</div>
</div>
)
}

const Button = ({ css, ...props }) => (
<div
css={{
alignItems: 'center',
color: colors.N60,
cursor: 'pointer',
display: 'flex ',
fontWeight: 300,
height: 32,
justifyContent: 'center',
marginLeft: 10,
position: 'relative',
textAlign: 'center',
minWidth: 32,

'&:hover, &:active': {
color: colors.N100,
},

...css,
}}
role="button"
{...props}
/>
)

type SvgProps = { size: number }

const Svg = ({ size, ...props }: SvgProps) => (
<svg
role="presentation"
viewBox="0 0 24 24"
css={{
display: 'inline-block',
fill: 'currentColor',
height: size,
stroke: 'currentColor',
strokeWidth: 0,
width: size,
}}
{...props}
/>
)

export const Close = ({ size = 24, ...props }: SvgProps) => (
<Svg size={size} {...props}>
<path d="M18.984 6.422l-5.578 5.578 5.578 5.578-1.406 1.406-5.578-5.578-5.578 5.578-1.406-1.406 5.578-5.578-5.578-5.578 1.406-1.406 5.578 5.578 5.578-5.578z" />
</Svg>
)
export const Heart = ({ size = 24, ...props }: SvgProps) => (
<Svg size={size} {...props}>
<path d="M12.094 18.563c4.781-4.313 7.922-7.172 7.922-10.078 0-2.016-1.5-3.469-3.516-3.469-1.547 0-3.047 0.984-3.563 2.344h-1.875c-0.516-1.359-2.016-2.344-3.563-2.344-2.016 0-3.516 1.453-3.516 3.469 0 2.906 3.141 5.766 7.922 10.078l0.094 0.094zM16.5 3c3.094 0 5.484 2.391 5.484 5.484 0 3.797-3.375 6.844-8.531 11.531l-1.453 1.313-1.453-1.266c-5.156-4.688-8.531-7.781-8.531-11.578 0-3.094 2.391-5.484 5.484-5.484 1.734 0 3.422 0.844 4.5 2.109 1.078-1.266 2.766-2.109 4.5-2.109z" />
</Svg>
)
152 changes: 152 additions & 0 deletions docs/pages/CustomComponents/ImageViewer/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
// @flow
// @jsx glam

import glam from 'glam'
import React, { Component } from 'react'
import Carousel, { Modal, ModalGateway } from '../../../../src/components'

import { Heading } from '../../components'
import { colors } from '../../../theme'
import { largeDevice } from '../../../utils'
import { Header } from './components'

const navButtonStyles = base => ({
...base,
backgroundColor: 'white',
boxShadow: '0 1px 6px rgba(0, 0, 0, 0.18)',
color: colors.N60,

'&:hover, &:active': {
backgroundColor: 'white',
color: colors.N100,
opacity: 1,
},
'&:active': {
boxShadow: '0 1px 3px rgba(0, 0, 0, 0.14)',
transform: 'scale(0.96)',
},
})

type Props = {}
type State = { currentModal: number | null }
export default class ImageViewer extends Component<Props, State> {
state = { currentModal: null }
toggleModal = (index: number | null = null) => {
this.setState({ currentModal: index })
}
render() {
const { images, isLoading } = this.props
const { currentModal } = this.state

return (
<div>
<Heading source="/CustomComponents/ImageViewer/index.js">Image Viewer à la Slack</Heading>
<p>Showing off the potential of component replacement with this knock off.</p>
{!isLoading ? (
<FilmStrip>
{images.map(({ caption, source }, j) => (
<Image onClick={() => this.toggleModal(j)} key={source.regular}>
<img
alt={caption}
src={source.thumbnail}
css={{
cursor: 'pointer',
position: 'absolute',
maxWidth: '100%',
}}
/>
</Image>
))}
</FilmStrip>
) : null}
<ModalGateway>
{Number.isInteger(currentModal) ? (
<Modal
allowFullscreen={false}
closeOnBackdropClick={false}
onClose={this.toggleModal}
styles={{
blanket: base => ({
...base,
backgroundColor: colors.N05,
}),
positioner: base => ({
...base,
display: 'block',
}),
}}
>
<Carousel
currentIndex={currentModal}
components={{ Footer: null, Header }}
views={images}
styles={{
container: base => ({
...base,
height: '100vh',
}),
view: base => ({
...base,
alignItems: 'center',
display: 'flex ',
height: 'calc(100vh - 54px)',
justifyContent: 'center',

[largeDevice]: {
padding: 20,
},

'& > img': {
maxHeight: 'calc(100vh - 94px)',
},
}),
navigationPrev: navButtonStyles,
navigationNext: navButtonStyles,
}}
/>
</Modal>
) : null}
</ModalGateway>
</div>
)
}
}

const gutter = 2

const FilmStrip = (props: any) => (
<div
css={{
borderBottom: `1px solid ${colors.N10}`,
borderTop: `1px solid ${colors.N10}`,
marginLeft: -gutter,
marginRight: -gutter,
overflowX: 'auto',
paddingBottom: 10,
paddingTop: 10,
WebkitOverflowScrolling: 'touch',
whiteSpace: 'nowrap',
}}
{...props}
/>
)

const Image = (props: any) => (
<div
css={{
backgroundColor: '#eee',
boxSizing: 'border-box',
display: 'inline-block',
margin: gutter,
overflow: 'hidden',
paddingBottom: '15%',
position: 'relative',
width: `calc(25% - ${gutter * 2}px)`,

':hover': {
opacity: 0.9,
},
}}
{...props}
/>
)
144 changes: 144 additions & 0 deletions docs/pages/CustomComponents/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
// @flow

import React, { Component } from 'react'
import { Helmet } from 'react-helmet'

import AlternativeMedia from './AlternativeMedia'
import ImageViewer from './ImageViewer'
import { Code, CodeBlock, Title } from '../components'

const propFn = k => {
const style = { display: 'inline-block', marginBottom: 4, marginRight: 4 }
return (
<span key={k} style={style}>
<Code>{k}</Code>
</span>
)
}
const commonProps = [
'carouselProps',
'currentIndex',
'currentView',
'frameProps',
'getStyles',
'isFullscreen',
'isModal',
'modalProps',
'interactionIsIdle',
'trackProps',
'views',
]

export default class CustomComponents extends Component<*> {
render() {
return (
<div>
<Helmet>
<title>Components - React Images</title>
<meta
name="description"
content="React Images allows you to augment layout and functionality by
replacing the default components with your own."
/>
</Helmet>
<Title>Components</Title>
<p>
The main feature of this library is providing consumers with the building blocks necessary to create <em>their</em> component.
</p>
<h3>Replacing Components</h3>
<p>
React-Images allows you to augment layout and functionality by replacing the default components with your own, using the <Code>components</Code>{' '}
property. These components are given all the current props and state letting you achieve anything you dream up.
</p>
<h3>Inner Props</h3>
<p>
All functional properties that the component needs are provided in <Code>innerProps</Code> which you must spread.
</p>
<h3>Common Props</h3>
<p>
Every component receives <Code>commonProps</Code> which are spread onto the component. These include:
</p>
<p>{commonProps.map(propFn)}</p>
<CodeBlock>
{`import React from 'react';
import Carousel from 'react-images';
const CustomHeader = ({ innerProps, isModal }) => isModal ? (
<div {...innerProps}>
// your component internals
</div>
) : null;
class Component extends React.Component {
render() {
return <Carousel components={{ Header: CustomHeader }} />;
}
}`}
</CodeBlock>

<h2>Component API</h2>

<h3>{'<Container />'}</h3>
<p>Wrapper for the carousel. Attachment point for mouse and touch event listeners.</p>

<h3>{'<Footer />'}</h3>
<p>
Element displayed beneath the views. Renders <Code>FooterCaption</Code> and <Code>FooterCount</Code> by default.
</p>

<h3>{'<FooterCaption />'}</h3>
<p>
Text associated with the current view. Renders <Code>{'{viewData.caption}'}</Code> by default.
</p>

<h3>{'<FooterCount />'}</h3>
<p>
How far through the carousel the user is. Renders{' '}
<Code>
{'{currentIndex}'}&nbsp;of&nbsp;{'{totalViews}'}
</Code>{' '}
by default
</p>

<h3>{'<Header />'}</h3>
<p>
Element displayed above the views. Renders <Code>FullscreenButton</Code> and <Code>CloseButton</Code> by default.
</p>

<h3>{'<HeaderClose />'}</h3>
<p>
The button to close the modal. Accepts the <Code>onClose</Code> function.
</p>

<h3>{'<HeaderFullscreen />'}</h3>
<p>
The button to fullscreen the modal. Accepts the <Code>toggleFullscreen</Code> function.
</p>

<h3>{'<Navigation />'}</h3>
<p>
Wrapper for the <Code>{'<NavigationNext />'}</Code> and <Code>{'<NavigationPrev />'}</Code> buttons.
</p>

<h3>{'<NavigationPrev />'}</h3>
<p>
Button allowing the user to navigate to the previous view. Accepts an <Code>onClick</Code> function.
</p>

<h3>{'<NavigationNext />'}</h3>
<p>
Button allowing the user to navigate to the next view. Accepts an <Code>onClick</Code> function.
</p>

<h3>{'<View />'}</h3>
<p>
The view component renders your media to the user. Receives the current view object as the <Code>data</Code> property.
</p>

<h2>Examples</h2>
<ImageViewer {...this.props} />
<AlternativeMedia />
</div>
)
}
}
78 changes: 78 additions & 0 deletions docs/pages/CustomStyles/CarouselExample.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// @flow
// @jsx glam
import glam from 'glam'
import React, { Component } from 'react'

import Carousel from '../../../src/components'
import type { ProviderProps } from '../../ImageProvider'
import { Code, FooterCaption, Heading } from '../components'
import { getAltText } from '../formatters'

export default class CarouselExample extends Component<ProviderProps> {
render() {
const { images, isLoading } = this.props

return (
<div>
<Heading source="/CustomStyles/CarouselExample.js">Carousel Example</Heading>
<p>
In this example some components are extended to appear like a polaroid. Various elements react to <Code>interactionIsIdle</Code> by dimming, changing
color or applying a CSS filter.
</p>
{!isLoading ? (
<Carousel
components={{ FooterCaption }}
formatters={{ getAltText }}
views={images}
styles={{
container: base => ({
...base,
backgroundColor: '#fafafa',
boxShadow: '0 1px 10px -1px rgba(0,0,0,0.2), 0 0 0 1px rgba(0,0,0,0.04), 0 1px 0 rgba(0,0,0,0.04)',
padding: 10,
}),
footer: (base, state) => ({
...base,
color: 'black',
minHeight: 42,
paddingBottom: 0,

'& a': {
color: state.interactionIsIdle ? 'black' : '#00d7ff',
transition: 'color 300ms',
},
'& strong': { textTransform: 'uppercase' },
}),
navigationItem: base => ({
...base,
backgroundColor: 'transparent',

':hover': {
backgroundColor: '#00d7ff',
},
':active': {
backgroundColor: '#00d7ff',
transform: 'translateY(2px)',
},
}),
view: (base, state) => ({
...base,
filter: state.interactionIsIdle ? 'grayscale(100%)' : null,
paddingBottom: `${(10 / 16) * 100}%`,
overflow: 'hidden',
position: 'relative',
transition: 'filter 300ms',

'& > img': {
position: 'absolute',
left: 0,
top: 0,
},
}),
}}
/>
) : null}
</div>
)
}
}
97 changes: 97 additions & 0 deletions docs/pages/CustomStyles/ModalExample.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// @flow
// @jsx glam
import glam from 'glam'
import React, { Component } from 'react'

import Carousel, { Modal, ModalGateway } from '../../../src/components'
import type { ProviderProps } from '../../ImageProvider'
import { Code, FooterCaption, Heading } from '../components'
import { getAltText } from '../formatters'

type State = { lightboxIsOpen: boolean }

export default class ModalExample extends Component<ProviderProps, State> {
state = { lightboxIsOpen: false }
toggleLightbox = () => {
this.setState(state => ({
lightboxIsOpen: !state.lightboxIsOpen,
}))
}
render() {
const { images, isLoading } = this.props
const { lightboxIsOpen } = this.state

return (
<div>
<Heading source="/CustomStyles/ModalExample.js">Modal Example</Heading>
<p>
In this example the <Code>blanket</Code>, <Code>footer</Code>, and <Code>header</Code> have been <em>inverted</em> from the default style of white on
black. The dialog has been given a <Code>maxWidth</Code> centering the entire element.
</p>

{!isLoading ? (
<button type="button" onClick={this.toggleLightbox}>
Open Modal
</button>
) : null}

<ModalGateway>
{!isLoading && lightboxIsOpen ? (
<Modal
allowFullscreen={false}
onClose={this.toggleLightbox}
styles={{
blanket: base => ({
...base,
backgroundColor: 'rgba(255,255,255,0.85)',
}),
dialog: base => ({
...base,
maxWidth: 640,
}),
}}
>
<Carousel
components={{ FooterCaption }}
formatters={{ getAltText }}
views={images}
styles={{
footer: base => ({
...base,
background: 'none !important',
color: '#666',
padding: 0,
paddingTop: 20,
position: 'static',

'& a': {
color: 'black',
},
}),
header: base => ({
...base,
background: 'none !important',
padding: 0,
paddingBottom: 10,
position: 'static',
}),
headerClose: base => ({
...base,
color: '#666',

':hover': { color: '#DE350B' },
}),
view: base => ({
...base,
maxHeight: 480,
overflow: 'hidden',
}),
}}
/>
</Modal>
) : null}
</ModalGateway>
</div>
)
}
}
120 changes: 120 additions & 0 deletions docs/pages/CustomStyles/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
// @flow
// @jsx glam
import glam from 'glam'
import React, { Component } from 'react'
import { Helmet } from 'react-helmet'

import type { ProviderProps } from '../../ImageProvider'
import type { RouterProps } from '../../../src/types'
import { Code, CodeBlock, Title } from '../components'
import CarouselExample from './CarouselExample'
import ModalExample from './ModalExample'

type Props = ProviderProps & RouterProps

const keyFn = k => {
const style = { display: 'inline-block', marginBottom: 4, marginRight: 4 }
return (
<span key={k} style={style}>
<Code>{k}</Code>
</span>
)
}
const carouselKeys = [
'container',
'footer',
'footerCaption',
'footerCount',
'header',
'headerClose',
'headerFullscreen',
'navigation',
'navigationPrev',
'navigationNext',
'view',
]
const modalKeys = ['blanket', 'dialog', 'positioner']

export default class CustomStyles extends Component<Props> {
render() {
return (
<div>
<Helmet>
<title>Styles - React Images</title>
<meta
name="description"
content="React Images offers a flexible, light-weight styling framework which
is a thin abstraction over simple javascript objects."
/>
</Helmet>
<Title>Styles</Title>
<p>
React-Images offers a flexible, light-weight styling framework which is a thin abstraction over simple javascript objects using{' '}
<a href="https://github.com/threepointone/glam" target="_blank">
glam
</a>
.
</p>
<CodeBlock>
{`/**
* @param {Object} base -- the component's default style
* @param {Object} state -- the component's current state e.g. \`isModal\`
* @returns {Object}
*/
function styleFn(base, state) {
// optionally spread base styles
return { ...base, color: state.isModal ? 'blue' : 'red' };
}`}
</CodeBlock>
<h3>Style Object</h3>
<p>
Each component is keyed, and ships with default styles. The component's default style object is passed as the first argument to the function when it's
resolved.
</p>
<p>
The second argument is the current state of the carousel, features like <Code>interactionIsIdle</Code>, <Code>isModal</Code> etc. allowing you to
implement dynamic styles for each of the components.
</p>
<h6>Carousel Keys</h6>
<p>{carouselKeys.map(keyFn)}</p>
<h6>Modal Keys</h6>
<p>{modalKeys.map(keyFn)}</p>
<h3>Base and State</h3>
<p>
Spreading the base styles into your returned object let's you extend it however you like while maintaining existing styles. Alternatively, you can
omit the base and completely take control of the component's styles.
</p>
<CodeBlock>
{`const customStyles = {
header: (base, state) => ({
...base,
borderBottom: '1px dotted pink',
color: state.isFullscreen ? 'red' : 'blue',
padding: 20,
}),
view: () => ({
// none of react-images styles are passed to <View />
height: 400,
width: 600,
}),
footer: (base, state) => {
const opacity = state.interactionIsIdle ? 0 : 1;
const transition = 'opacity 300ms';
return { ...base, opacity, transition };
}
}
const App = () => (
<Carousel
styles={customStyles}
views={...}
/>
);`}
</CodeBlock>
<CarouselExample {...this.props} />
<ModalExample {...this.props} />
</div>
)
}
}
93 changes: 93 additions & 0 deletions docs/pages/Home/GalleryExample.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// @flow
// @jsx glam
import glam from 'glam'
import React, { Component, Fragment } from 'react'

import { type ProviderProps } from '../../ImageProvider'
import Carousel, { Modal, ModalGateway } from '../../../src/components'
import { FooterCaption } from '../components'
import { getAltText } from '../formatters'

type State = {
selectedIndex?: number,
lightboxIsOpen: boolean,
}

export default class Home extends Component<ProviderProps, State> {
state = {
selectedIndex: 0,
lightboxIsOpen: false,
}
toggleLightbox = (selectedIndex: number) => {
this.setState(state => ({
lightboxIsOpen: !state.lightboxIsOpen,
selectedIndex,
}))
}
render() {
const { images, isLoading } = this.props
const { selectedIndex, lightboxIsOpen } = this.state
return (
<Fragment>
{!isLoading ? (
<Gallery>
{images.map(({ caption, source }, j) => (
<Image onClick={() => this.toggleLightbox(j)} key={source.thumbnail}>
<img
alt={caption}
src={source.thumbnail}
css={{
cursor: 'pointer',
position: 'absolute',
maxWidth: '100%',
}}
/>
</Image>
))}
</Gallery>
) : null}

<ModalGateway>
{lightboxIsOpen && !isLoading ? (
<Modal onClose={this.toggleLightbox}>
<Carousel components={{ FooterCaption }} currentIndex={selectedIndex} formatters={{ getAltText }} views={images} />
</Modal>
) : null}
</ModalGateway>
</Fragment>
)
}
}

const gutter = 2

const Gallery = (props: any) => (
<div
css={{
overflow: 'hidden',
marginLeft: -gutter,
marginRight: -gutter,
}}
{...props}
/>
)

const Image = (props: any) => (
<div
css={{
backgroundColor: '#eee',
boxSizing: 'border-box',
float: 'left',
margin: gutter,
overflow: 'hidden',
paddingBottom: '16%',
position: 'relative',
width: `calc(25% - ${gutter * 2}px)`,

':hover': {
opacity: 0.9,
},
}}
{...props}
/>
)
97 changes: 97 additions & 0 deletions docs/pages/Home/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// @flow
// @jsx glam
import glam from 'glam'
import React, { Component } from 'react'

import { type ProviderProps } from '../../ImageProvider'
import { Code, CodeBlock, Heading, Title } from '../components'
import PrettyProps from '../../PrettyProps'
import GalleryExample from './GalleryExample'
import { carouselProps, modalProps } from './props'

export default class Home extends Component<ProviderProps> {
render() {
return (
<div>
<Title>React Images</Title>
<p>
A mobile-friendly, highly customizable, carousel component for displaying media in ReactJS. Images courtesy of{' '}
<a href="https://unsplash.com" target="_blank">
Unsplash
</a>
.
</p>

<h3>Getting Started</h3>
<p>
Start by installing <Code>react-images</Code>
</p>
<CodeBlock>yarn add react-images</CodeBlock>

<h3>Using the Carousel</h3>
<p>
Import the carousel from <Code>react-images</Code> at the top of a component and then use it in the render function.
</p>
<CodeBlock>{`import React from 'react';
import Carousel from 'react-images';
const images = [{ source: 'path/to/image-1.jpg' }, { source: 'path/to/image-2.jpg' }];
class Component extends React.Component {
render() {
return <Carousel views={images} />;
}
}`}</CodeBlock>

<h3>Using the Modal</h3>
<p>
Import the modal and optionally the modal gateway from <Code>react-images</Code> at the top of a component and then use it in the render function.
</p>
<p>
The <Code>ModalGateway</Code> will insert the modal just before the end of your <Code>{'<body />'}</Code> tag.
</p>
<CodeBlock>{`import React from 'react';
import Carousel, { Modal, ModalGateway } from 'react-images';
const images = [{ src: 'path/to/image-1.jpg' }, { src: 'path/to/image-2.jpg' }];
class Component extends React.Component {
state = { modalIsOpen: false }
toggleModal = () => {
this.setState(state => ({ modalIsOpen: !state.modalIsOpen }));
}
render() {
const { modalIsOpen } = this.state;
return (
<ModalGateway>
{modalIsOpen ? (
<Modal onClose={this.toggleModal}>
<Carousel views={images} />
</Modal>
) : null}
</ModalGateway>
);
}
}`}</CodeBlock>

<Heading source="/Home/GalleryExample.js">Example Gallery</Heading>
<p>
Below is a pretty typical implementation; the index of the selected thumbnail is passed to the <Code>currentIndex</Code> property of the carousel. All
of the components, styles, getters, animations and functionality are the defaults provided by the library.
</p>
<GalleryExample {...this.props} />

<h2>Carousel Props</h2>
{carouselProps.map(p => (
<PrettyProps key={p.name} {...p} />
))}

<h2>Modal Props</h2>
{modalProps.map(p => (
<PrettyProps key={p.name} {...p} />
))}
</div>
)
}
}
279 changes: 279 additions & 0 deletions docs/pages/Home/props.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,279 @@
// @flow
import React, { Fragment } from 'react'
import { Link } from 'react-router-dom'

const viewPagerUrl = 'https://github.com/souporserious/react-view-pager'

export const carouselProps = [
{
defaultValue: null,
description: (
<p>
Replace any or all of the carousel components with your own to create the layout and functionality you desire. Detailed documentation on the{' '}
<Link to="/components">Components page</Link>.
</p>
),
isRequired: false,
name: 'components',
type: 'Object<ComponentType>',
typeDefinition: `// FrameProps, ModalProps, StyleFn, TrackProps, and ViewType declared below
type CommonProps = {
carouselProps: Object,
currentIndex: number,
currentView: ViewType,
frameProps FrameProps,
getStyles: StyleFn,
innerProps: Object,
isFullscreen: boolean,
isModal: boolean,
modalProps: ModalProps,
interactionIsIdle: boolean,
trackProps: TrackProps,
views: Array<ViewType>,
}
type components = {
Container: ComponentType<CommonProps>,
Footer: ComponentType<CommonProps>,
FooterCaption: ComponentType<CommonProps>,
FooterCount: ComponentType<CommonProps>,
Header: ComponentType<CommonProps>,
HeaderClose: ComponentType<CommonProps>,
HeaderFullscreen: ComponentType<CommonProps>,
Navigation: ComponentType<CommonProps>,
NavigationPrev: ComponentType<CommonProps>,
NavigationNext: ComponentType<CommonProps>,
View: ComponentType<CommonProps>,
}`,
},
{
defaultValue: '0',
description:
'React-Images manages the index state internally. This property is exposed so you can take control of it, or initiate the carousel from a certain point.',
isRequired: false,
name: 'currentIndex',
type: 'Number',
},
{
defaultValue: null,
description: (
<p>
Passed on to react-view-pager's Frame component. Check out{' '}
<a href={`${viewPagerUrl}#frame-props`} target="_blank">
#frame-props
</a>{' '}
for more information.
</p>
),
isRequired: false,
name: 'frameProps',
type: 'Object',
typeDefinition: `{
accessibility: boolean,
autoSize: true | false | 'width' | 'height',
springConfig: { [key: string]: number },
tag: string,
}`,
},
{
description:
'Formatters get called with common props to render labels and titles for certain components. Replace the default formatters if you want to tweak the text or change the language.',
isRequired: false,
name: 'formatters',
type: 'Object<Function>',
typeDefinition: `// CommonProps declared above in the \`component\` prop type
{
getAltText: (CommonProps) => string, // {caption} | Image {currentIndex}
getNextLabel: (CommonProps) => string, // Show slide {nextIndex} of {totalCount}
getPrevLabel: (CommonProps) => string, // Show slide {prevIndex} of {totalCount}
getNextTitle: (CommonProps) => string, // Next (right arrow)
getPrevTitle: (CommonProps) => string, // Previous (left arrow)
getCloseLabel: (CommonProps) => string, // Close (esc)
getFullscreenLabel: (CommonProps) => string, // [Enter | Exit] fullscreen (f)
}`,
},
{
defaultValue: '3000',
description: 'The duration, in milliseconds, to wait before hiding controls when the user is idle.',
isRequired: false,
name: 'hideControlsWhenIdle',
type: 'Number | false',
},
{
defaultValue: null,
description: 'Available when the Carousel is within a Modal. The applicable props are cloned and passed on for use inside Carousel components.',
isRequired: false,
name: 'modalProps',
type: 'Object',
typeDefinition: `{
allowFullscreen: boolean,
isFullscreen: boolean,
onClose: (SyntheticEvent) => void,
preventScroll: boolean,
toggleFullscreen: () => void,
}`,
},
{
defaultValue: 'false',
description: 'Whether image carousel navigation buttons should be hidden or shown on touch-enabled devices. (Default: hidden)',
isRequired: false,
name: 'showNavigationOnTouchDevice',
type: 'boolean',
},
{
defaultValue: null,
description: 'React-Images ships each Carousel component with default styles. You can extend or replace these using the styles property.',
isRequired: false,
name: 'styles',
type: 'Object<Function>',
typeDefinition: `type StyleObj = { [key: string]: any }
type State = {
isFullscreen: boolean,
isModal: boolean,
interactionIsIdle: boolean,
}
type StyleFn = (StyleObj, State) => StyleObj
{
container: StyleFn,
footer: StyleFn,
footerCaption: StyleFn,
footerCount: StyleFn,
header: StyleFn,
headerClose: StyleFn,
headerFullscreen: StyleFn,
navigation: StyleFn,
navigationPrev: StyleFn,
navigationNext: StyleFn,
view: StyleFn,
}`,
},
{
defaultValue: "{ swipe: 'touch' }",
description: (
<p>
Passed on to react-view-pager's Track component. Check out{' '}
<a href={`${viewPagerUrl}#track-props`} target="_blank">
#track-props
</a>{' '}
for more information.
</p>
),
isRequired: false,
name: 'trackProps',
type: 'Object',
typeDefinition: `{
align: number,
animations: Array<{ props: string, stops: Array<[number, number]> }>,
axis: 'x' | 'y',
contain: boolean,
currentView: any,
flickTimeout: number,
infinite: boolean,
instant: boolean,
onRest: () => void,
onScroll: () => void,
onSwipeEnd: () => void,
onSwipeMove: () => void,
onSwipeStart: () => void,
onViewChange: number => void,
springConfig: { [key: string]: number },
swipe: true | false | 'mouse' | 'touch',
swipeThreshold: number,
tag: any,
viewsToMove: number,
viewsToShow: number | 'auto',
}`,
},
{
defaultValue: null,
description: (
<Fragment>
<p>
The data shape for each view in your carousel. This must be an array of objects, though the key/value pairs in the objects can be whatever you want.
</p>
<p>When using "non-standard" view data you must provide a View component that can interpret and render it.</p>
</Fragment>
),
isRequired: true,
name: 'views',
type: 'Array<Object>',
typeDefinition: `// the default View component expects
Array<{
caption?: string | Node,
source: string | {
download?: string,
fullscreen?: string,
regular: string,
thumbnail?: string,
},
}>`,
},
]

export const modalProps = [
{
defaultValue: 'true',
description: 'Whether the user should be allowed to fullscreen the dialog, either by clicking the Fullscreen button or from an `F` keypress.',
name: 'allowFullscreen',
type: 'Boolean',
},
{
description: 'Modal expects a single Carousel child. It will not behave as expected otherwise.',
name: 'children',
type: 'CarouselType',
isRequired: true,
},
{
defaultValue: 'true',
description: 'Whether the `onClose` function should be called when the backdrop is clicked.',
name: 'closeOnBackdropClick',
type: 'Boolean',
},
{
defaultValue: 'true',
description: 'Whether the `onClose` function should be called when the `esc` key is pressed',
name: 'closeOnEsc',
type: 'Boolean',
},
{
description: 'Function called to request close of the modal',
isRequired: true,
name: 'onClose',
type: 'Function',
typeDefinition: '(Event) => void',
},
{
description: 'React-Images ships each Modal component with default styles. You can extend or replace these using the styles property.',
name: 'styles',
type: 'Object<Function>',
typeDefinition: `type StyleObj = { [key: string]: any }
type State = { isFullscreen: boolean }
type StyleFn = (StyleObj, State) => StyleObj
{
blanket: StyleFn,
dialog: StyleFn,
positioner: StyleFn,
}`,
},
{
defaultValue: 'true',
description: (
<p>
Determines whether scrolling is prevented via{' '}
<a href="https://github.com/jossmac/react-scrolllock" target="_blank">
react-scrolllock
</a>
.
</p>
),
name: 'preventScroll',
type: 'Boolean',
},
]
13 changes: 13 additions & 0 deletions docs/pages/NoMatch.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// @flow
import React from 'react'
import { Link } from 'react-router-dom'

export default function NoMatch() {
return (
<div>
<h1>Oops!</h1>
<p>Couldn&apos;t find this page.</p>
<Link to="/">Back home</Link>
</div>
)
}
53 changes: 53 additions & 0 deletions docs/pages/Patterns/RouterGallery.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// @flow
// @jsx glam
import glam from 'glam'
import React, { Component } from 'react'

import Carousel from '../../../src/components'
import type { ProviderProps } from '../../ImageProvider'
import type { RouterProps } from '../../../src/types'
import { Code, Heading } from '../components'

type Props = ProviderProps & RouterProps

export default class RouterGallery extends Component<Props> {
handleViewChange = (currentIndex: number) => {
const { history } = this.props

// do not influence history on browser back/forward
if (history.action === 'POP') return

history.push(`/patterns/${currentIndex.toString()}`)
}
getCurrentView() {
const { match } = this.props
return match ? parseInt(match.params.currentIndex, 10) || 0 : 0
}
render() {
const { images, isLoading } = this.props

return (
<div>
<Heading source="/Patterns/RouterGallery.js">Carousel with Routing</Heading>
<p>
In this example we sync the <Code>currentIndex</Code> with the URL using react-router's <Code>history.push</Code> method. Check out the source to see
this technique in detail.
</p>
<p>
Try navigating with the Carousel's buttons, then with the browser's back and forward buttons. The current view is also maintained on page refresh via
react-router's <Code>match.params</Code>.
</p>
{!isLoading ? (
<Carousel
currentIndex={this.getCurrentView()}
trackProps={{
onViewChange: this.handleViewChange,
}}
views={images}
components={{ Header: null }}
/>
) : null}
</div>
)
}
}
28 changes: 28 additions & 0 deletions docs/pages/Patterns/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// @flow
// @jsx glam
import glam from 'glam'
import React, { Component } from 'react'
import { Helmet } from 'react-helmet'

import { Title } from '../components'
import RouterGallery from './RouterGallery'

export default class Patterns extends Component<*> {
render() {
return (
<div>
<Helmet>
<title>Patterns - React Images</title>
<meta
name="description"
content="A collection of common patterns you may like to implement with
React-Images."
/>
</Helmet>
<Title>Patterns</Title>
<p>A collection of common patterns you may like to implement with React-Images.</p>
<RouterGallery {...this.props} />
</div>
)
}
}
33 changes: 33 additions & 0 deletions docs/pages/Thanks/data.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import React from 'react'

export const features = [
{
icon: '🛠',
text: 'Static type checking',
link: (
<a href="https://flow.org" target="_blank">
flow
</a>
),
},
{
icon: '📱',
text: 'Support for touch devices',
link: (
<a href="http://souporserious.github.io/react-view-pager" target="_blank">
react-view-pager
</a>
),
},
{
icon: '📺',
text: 'Vendor agnostic fullscreen support',
link: (
<a href="https://github.com/snakesilk/react-fullscreen" target="_blank">
react-full-screen
</a>
),
},
]

export type FeaturesType = typeof features
66 changes: 66 additions & 0 deletions docs/pages/Thanks/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// @flow
// @jsx glam
import glam from 'glam'
import React, { Component } from 'react'

import { Title } from '../components'
import { features } from './data'
import { colors } from '../../theme'

const Hr = () => (
<hr
css={{
backgroundColor: colors.N10,
border: 0,
height: 2,
marginBottom: '2em',
marginTop: '2em',
}}
/>
)

export default class Thanks extends Component<*> {
render() {
return (
<div>
<Title>Thanks</Title>
<p>
I&apos;ve spent <em>a lot of time</em> in R&amp;D with{' '}
<a href="https://twitter.com/JedWatson" target="_blank">
@JedWatson
</a>{' '}
exploring component architecture and styling possibilities for version two of{' '}
<a href="https://github.com/JedWatson/react-select" target="_blank">
react-select
</a>
. This project has directly benefitted from the solutions discovered in that process &mdash; cheers mate!
</p>
<p>
A big thank you to{' '}
<a href="https://twitter.com/thethinkmill" target="_blank">
@TheThinkmill
</a>{' '}
for embracing open-source and encouraging its employees to give back.
</p>
<Hr />
<p>Shout out to the following projects that allowed me to build this:</p>
<List items={features} />
<p>
I&apos;d also like to thank everyone who has contributed to this project over the years, without your support react-images wouldn&apos;t be where it
is today. Cheers to the open source community!
</p>
</div>
)
}
}

const List = ({ items }: FeaturesType) => (
<ul>
{items.map(({ link, text }, j) => (
<li key={j}>
{text}
{link ? <span> &mdash; {link}</span> : null}
</li>
))}
</ul>
)
Loading