[
  {
    "path": ".babelrc",
    "content": "{\n  \"ignore\": [\"node-modules\"],\n  \"presets\": [\"env\", \"react\"],\n  \"plugins\": [ [\"transform-object-rest-spread\", { \"useBuiltIns\": true }], \"transform-object-assign\"],\n  \"env\": {\n    \"test\": {\n      \"presets\": [\n        [\"env\"], \"react\"\n      ],\n      \"plugins\": [\"istanbul\"]\n    }\n  }\n}\n"
  },
  {
    "path": ".coveralls.yml",
    "content": "service_name: travis-ci\nrepo_token: S5CSBnH2886RuV3s0ecSjaam2N4JOjsC7\n# coverageReporter: {\n#     dir: path.join(__dirname, 'coverage'),\n#     reporters: [\n#         {type: 'html'},\n#         {type: 'lcov', subdir: 'lcov'}  // lcov\n#     ]\n# },\n\n"
  },
  {
    "path": ".editorconfig",
    "content": "\n# This file is for unifying the coding style for different editors and IDEs\n# editorconfig.org\nroot = true\n [*]\nend_of_line = lf\ncharset = utf-8\ntrim_trailing_whitespace = true\ninsert_final_newline = true\nindent_style = space\nindent_size = 2\n [*.md]\ntrim_trailing_whitespace = false\n"
  },
  {
    "path": ".eslintignore",
    "content": "lib/*\ndist/*\ncoverage/*\nnode_modules/*\n"
  },
  {
    "path": ".eslintrc.js",
    "content": "// https://eslint.org/docs/user-guide/configuring\n\nmodule.exports = {\n  root: true,\n  parser: 'babel-eslint',\n  parserOptions: {\n    ecmaFeatures: {\n      jsx: true,\n    },\n  },\n  parserOptions: {\n    sourceType: 'module'\n  },\n  env: {\n    node: true,\n    browser: true,\n    commonjs: true,\n    es6: true,\n    jest: true,\n  },\n  extends: [\n    'eslint:recommended',\n    'plugin:react/recommended',\n  ],\n  // required to lint *.vue files\n  plugins: [\n    'react',\n  ],\n  // check if imports actually resolve\n  settings: {\n    react: {\n      createClass: 'createReactClass',\n      pragma: 'React',\n      version: '16.4.1'\n    },\n  },\n  rules: {\n    'no-unused-vars': 0,\n    // allow debugger during development\n    'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,\n    'no-console': process.env.NODE_ENV === 'production' ? 2 : 0,\n    \"no-unused-vars\": [2, { \"vars\": \"all\", \"args\": \"after-used\" }],\n    'global-require': 0,\n  }\n}\n"
  },
  {
    "path": ".github/CONTRIBUTING.md",
    "content": "# Contributing\n\nThanks for your interest in react-images-viewer. All forms fo contribution ares welcome, from issue reports to PRs and documentation / write-ups.\n\nBefore you open a PR:\n\n* 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.\n\n* In development, run `yarn start` to build(+watch) the project source, and run the [development server](http://localhost:8000).\n\n* 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.\n* Please  **DO NOT** commit the build files. Make sure **ONLY** your changes to `/src/` and `/examples/src` are included in your PR.\n\n* \bBe careful to follow the code style of the project. Run `yarn ru lint` after your changes and ensure you do not introduce any new errors or warnings.\n\n* All new features and changes need documentation.\n\n* Make sure yarn.lock is updated when you add or upgrade dependencies.\n"
  },
  {
    "path": ".github/HISTORY.md",
    "content": "# React-Images-Viewer\n\n### v1.6.8 / 2022-01-03\n\n- Update: fix issues: #28, #38\n\n### v1.3.0 / 2018-07-24\n\n- Update: Make README.md and README_ZN.md desciption more specific\n- Fix: correct `package.json - main` field value\n\n---\n\n### v1.0.5 / 2018-07-24\n\n- Basic, thumbnailed, themed usage all work well\n- Feature: you can use `up, right, bottom, left, space, esc` to navigate quickly\n- Component API is very brief and precise\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE.md",
    "content": "**Steps to reproduce the behavior:**\n\n**Expected behavior:**\n\n**Actual behavior:**\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "content": "<!-- Please make sure the following is filled in before submitting your Pull Request, thanks! -->\n\n**Description of changes:**\n\n**Related issues (if any):**\n\n**Checks:**\n\n- [ ] Please confirm `yarn run lint` ran successfully\n- [ ] Please confirm that only `/src` and `/examples/src` are committed\n- [ ] [if new feature] Please confim that docuemtation was added to the README.md\n"
  },
  {
    "path": ".gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n\n# Build\nlib\ndist\nexamples/dist\n\n# Webstorm\n.idea/\n\n# VSCode\n.vscode/\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n\n# nyc test coverage\n.nyc_output\n\n# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# node-waf configuration\n.lock-wscript\n\n# Bower dependency directory (https://bower.io/)\nbower_components\n\n# node-waf configuration\n.lock-wscript\n\n# Compiled binary addons (https://nodejs.org/api/addons.html)\nbuild/Release\n\n# Site build\n.publish\n\n# Dependency directories\n# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git\nnode_modules/\njspm_packages/\n\n# temp files\n.swp\n\n# TypeScript v1 declaration files\ntypings/\n\n# Optional npm cache directory\n.npm\n\n# Optional eslint cache\n.eslintcache\n\n# Optional REPL history\n.node_repl_history\n\n# Output of 'npm pack'\n*.tgz\n\n# Yarn Integrity file\n.yarn-integrity\n\n# dotenv environment variables file\n.env\n\n# next.js build output\n.next\n\n__test__/__snapshots__/\n\n.js.snap\n\n# MacOS generated file\n.DS_Store\n"
  },
  {
    "path": ".npmignore",
    "content": "examples\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: node_js\nsudo: false\nnode_js:\n  - stable\ncache:\n  directories:\n    - node_modules\ninstall:\n|\n  npm install -g yarn\n  npm --version\n  yarn\n\nscript:\n  - yarn lint\n  - yarn test\n  - yarn coverage\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2018 yiminanci\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# react-images-viewer\n\n[![Build Status](https://travis-ci.org/guonanci/react-images-viewer.svg?branch=master)](https://travis-ci.org/guonanci/react-images-viewer)\n\n<!-- [![Coverage Status](https://coveralls.io/repos/github/guonanci/react-images-viewer/badge.svg?branch=master)](https://coveralls.io/github/guonanci/react-images-viewer?branch=master) -->\n\n[![jest](https://jestjs.io/img/jest-badge.svg)](https://github.com/facebook/jest)\n\nA react library that view photos list easily, and a simple, responsive viewer component for displaying an array of images.\n\n[中文文档](./README_CN.md)\n\n## Quick start\n\n```bash\n# recommended\nyarn add react-images-viewer\n```\n\nor\n\n```bash\nnpm install react-images-viewer --save\n```\n\n```jsx\nimport React from \"react\";\nimport ImgsViewer from \"react-images-viewer\";\n\nexport default class Demo extends React.Component {\n  render() {\n    return (\n      <ImgsViewer\n        imgs={[\n          { src: \"http://example.com/img1.jpg\" },\n          { src: \"http://example.com/img2.png\" },\n        ]}\n        currImg={this.state.currImg}\n        isOpen={this.state.viewerIsOpen}\n        onClickPrev={this.gotoPrevious}\n        onClickNext={this.gotoNext}\n        onClose={this.closeViewer}\n      />\n    );\n  }\n}\n```\n\n## Demo & Example\n\nLive Demo: [guonanci.github.io/react-images-viewer](https://guonanci.github.io/react-images-viewer)\n\n```bash\nyarn install\nyarn\nyarn start\n```\n\nThen open [`localhost:8000`](http://localhost:8000) in a browser.\n\n### Using srcSet\n\nExample using srcSet:\n\n```jsx\n<ImgsViewer\n  imgs={IMG_SET}\n  ...\n/>\n```\n\n```js\nconst IMG_SET = [\n  {\n    src: \"http://example.com/img1.svg\",\n    caption: \"A forest\",\n    // As an array\n    srcSet: [\n      \"http://example.com/img1_1024.jpg 1024w\",\n      \"http://example.com/img1_800.jpg 800w\",\n      \"http://example.com/img1_500.jpg 500w\",\n      \"http://example.com/img1_320.jpg 320w\",\n    ],\n  },\n  {\n    src: \"http://example.com/img2.svg\",\n    // As a string\n    srcSet:\n      \"http://example.com/img2_1024.jpg 1024w, http://example.com/img2_800.jpg 800w, http://example.com/img2_500.jpg 500w, http://example.com/img2_320.jpg 320w\",\n  },\n];\n```\n\n## Options\n\n| Property            | Type       | Default          | Description                                                                                                                     |\n| :------------------ | :--------- | :--------------- | :------------------------------------------------------------------------------------------------------------------------------ |\n| backdropCloseable   | `boolean`  | `false`          | Allow users to exit the viewer by clicking the backdrop                                                                         |\n| closeBtnTitle       | `str`      | `undefined`      | Customize close esc title                                                                                                       |\n| enableKeyboardInput | `boolean`  | `true`           | Supports keyboard input - <code>space, esc</code>, <code> arrow left, arrow up</code>, and <code>arrow right, arrow down</code> |\n| currImg             | `number`   | `0`              | Required if you want to navigate the imgsViewer, The index of the image to display initially                                    |\n| customControls      | arr        | `undefined`      | An array of elements to display as custom controls on the top of viewer                                                         |\n| imgs                | arr        | `undefined`      | Required. Array of image objects, See img opts table below                                                                      |\n| imgCountSeparator   | `str`      | `' / '`          | Customize separator in the image count                                                                                          |\n| isOpen              | `boolean`  | `false`          | Required if you want to navigate the imgsViewer, Whether or not the viewer is displayed                                         |\n| leftArrowTitle      | `str`      | `undefined`      | Customize of left arrow title                                                                                                   |\n| onClickPrev         | `function` | `undefined`      | Required if you want to navigate the imgsViewer, and fired on request of the previous image                                     |\n| onClickNext         | `function` | `undefined`      | Required if you want to navigate the imgsViewer, and fired on request of the next image                                         |\n| onClose             | `function` | `undefined`      | Required if you want to close the imgsViewer, and handle closing of the viewer                                                  |\n| onClickImg          | `function` | `undefined`      | Handle click on current image                                                                                                   |\n| onClickThumbnail    | `function` | `undefined`      | Handle click on thumbnail                                                                                                       |\n| preloadNextImg      | `boolean`  | `true`           | Whether to preload the next available image                                                                                     |\n| rightArrowTitle     | `str`      | `undefined`      | Customize right arrow title                                                                                                     |\n| showCloseBtn        | `boolean`  | `true`           | Optionally display a close 'X' button in top right corner                                                                       |\n| showImgCount        | `boolean`  | `true`           | Optionally display image index, e.g., \"2 of 20\"                                                                                 |\n| width               | `number`   | `1024`           | Maximum width of the carousel; defaults to 1024px                                                                               |\n| spinnerDisabled     | `boolean`  | `false`          | Disable Spinner                                                                                                                 |\n| spinner             | `function` | `DefaultSpinner` | Spinner component class                                                                                                         |\n| spinnerColor        | `str`      | `'#fff'`         | Color of spinner                                                                                                                |\n| spinnerSize         | `number`   | `50`             | Size of spinner                                                                                                                 |\n| preventAutoScroll   | `boolean`  | `true`           | Determines whether auto-scrolling is prevented                                                                                  |\n\n## Images Object\n\n| Property | Type       | Default     | Description |\n| :------- | :--------  | :---------- | :---------- |\n| src      | `str`      | `undefined` | Required    |\n| srcSet   | `string[]` | `undefined` | Optional    |\n| caption  | `str`      | `undefined` | Optional    |\n| alt      | `str`      | `undefined` | Optional    |\n"
  },
  {
    "path": "README_CN.md",
    "content": "# react-images-viewer\n\n[![Build Status](https://travis-ci.org/guonanci/react-images-viewer.svg?branch=master)](https://travis-ci.org/guonanci/react-images-viewer)\n\n<!-- [![Coverage Status](https://coveralls.io/repos/github/guonanci/react-images-viewer/badge.svg?branch=master)](https://coveralls.io/github/guonanci/react-images-viewer?branch=master) -->\n\n[![jest](https://jestjs.io/img/jest-badge.svg)](https://github.com/facebook/jest)\n\n一个简单易用，响应式，放大并查看一组图片的 React 库。\n\n[English document](./README.md)\n\n## 快速入门\n\n```bash\n# 推荐\nyarn add react-images-viewer\n```\n\nor\n\n```bash\nnpm install react-images-viewer --save\n```\n\n```jsx\nimport React from \"react\";\nimport ImgsViewer from \"react-images-viewer\";\n\nexport default class Demo extends React.Component {\n  render() {\n    return (\n      <ImgsViewer\n        imgs={[\n          { src: \"http://example.com/img1.jpg\" },\n          { src: \"http://example.com/img2.png\" },\n        ]}\n        currImg={this.state.currImg}\n        isOpen={this.state.viewerIsOpen}\n        onClickPrev={this.gotoPrevious}\n        onClickNext={this.gotoNext}\n        onClose={this.closeViewer}\n      />\n    );\n  }\n}\n```\n\n## 例子\n\n线上案例: [guonanci.github.io/react-images-viewer](https://guonanci.github.io/react-images-viewer)\n\n```bash\nyarn install\nyarn\nyarn start\n```\n\n然后就可以在浏览器打开 [`localhost:8000`](http://localhost:8000) 窗口了。\n\n### srcSet 使用\n\n例子:\n\n```jsx\n<Lightbox\n  imgs={IMG_SET}\n  ...\n>\n```\n\n```js\nconst IMG_SET = [\n  {\n    src: \"http://example.com/img1.svg\",\n    caption: \"A forest\",\n    // As an array\n    srcSet: [\n      \"http://example.com/img1_1024.jpg 1024w\",\n      \"http://example.com/img1_800.jpg 800w\",\n      \"http://example.com/img1_500.jpg 500w\",\n      \"http://example.com/img1_320.jpg 320w\",\n    ],\n  },\n  {\n    src: \"http://example.com/img2.svg\",\n    // As a string\n    srcSet:\n      \"http://example.com/img2_1024.jpg 1024w, http://example.com/img2_800.jpg 800w, http://example.com/img2_500.jpg 500w, http://example.com/img2_320.jpg 320w\",\n  },\n];\n```\n\n## 选项\n\n| Property            | Type       | Default          | Description                                                                                         |\n| :------------------ | :--------- | :--------------- | :-------------------------------------------------------------------------------------------------- |\n| backdropCloseable   | `布尔值`   | `false`          | 是否通过点击半透明幕布来退出浏览                                                                    |\n| closeBtnTitle       | `字符串`   | `undefined`      | 关闭按钮的 title                                                                                    |\n| enableKeyboardInput | `布尔值`   | `true`           | 支持键盘输入 - <code>空格键，esc</code>, <code>左箭头，上箭头</code>，和<code>右箭头，下箭头</code> |\n| currImg             | `数值类型` | `0`              | 必须项（如果需要导航的话），初始化图像的索引                                                        |\n| customCtrls         | `数组`     | `undefined`      | 图片查看器顶部的控件元素数组                                                                        |\n| imgs                | `数组`     | `undefined`      | 必须项. 图片元素数组，图像选项见下表。                                                              |\n| imgsSeparator       | `字符串`   | `' / '`          | 图片计数分隔符                                                                                      |\n| isOpen              | `布尔值`   | `false`          | 必须项（如果需要导航的话），图片是否显示                                                            |\n| leftArrowTitle      | `字符串`   | `undefined`      | 左箭头的 title                                                                                      |\n| onClickPrev         | `函数`     | `undefined`      | 必须项（如果需要导航的话），请求上一张时触发                                                        |\n| onClickNext         | `函数`     | `undefined`      | 必须项（如果需要导航的话），请求下一张时触发                                                        |\n| onClose             | `函数`     | `undefined`      | 关闭查看器的回调                                                                                    |\n| onClickImg          | `函数`     | `undefined`      | 点起当前图片的回调                                                                                  |\n| onClickThumbnail    | `函数`     | `undefined`      | 缩略点击的回调                                                                                      |\n| preloadNextImg      | `布尔值`   | `true`           | 是否预加载下一张图片                                                                                |\n| rightArrowTitle     | `字符串`   | `undefined`      | 右箭头的 title                                                                                      |\n| showCloseBtn        | `布尔值`   | `true`           | 右上角是否显示 X 按钮                                                                               |\n| showImgCount        | `布尔值`   | `true`           | 是否显示图片索引                                                                                    |\n| width               | `数值类型` | `1024`           | 轮播器的最大的宽度，默认值 1024px                                                                   |\n| spinnerDisabled     | `布尔值`   | `false`          | 禁用加载器                                                                                          |\n| spinner             | `函数`     | `DefaultSpinner` | 加载器组件                                                                                          |\n| spinnerColor        | `字符串`   | `'#fff'`         | 加载器颜色                                                                                          |\n| spinnerSize         | `数值类型` | `50`             | 加载器尺寸                                                                                          |\n| preventAutoScroll   | `布尔值`   | `true`           | 是否阻止自动滚动                                                                                    |\n\n## Images Object\n\n| Property | Type                   | Default     | Description |\n| :------- | :--------------------- | :---------- | :---------- |\n| src      | `字符串`               | `undefined` | 必须项      |\n| srcSet   | `字符串`数组或者字符串 | `undefined` | 可选项      |\n| caption  | `字符串`               | `undefined` | 可选项      |\n| alt      | `字符串`               | `undefined` | 可选项      |\n"
  },
  {
    "path": "__mocks__/ImgsViewer.js",
    "content": ""
  },
  {
    "path": "__test__/Gallery.test.js",
    "content": "import React from 'react'\nimport ImgsViewer from '../src/ImgsViewer'\nimport { mount } from 'enzyme'\n\nconst props1 = {\n  onClose: () => {},\n  imgs: [\n    {\n      src: 'https://images.unsplash.com/photo-1526382551041-3c817fc3d478?dpr=2&auto=format&w=1024&h=1024'\n    },\n    {\n      src: 'https://images.unsplash.com/photo-1522985225914-17a10a58c8ec?dpr=2&auto=format&w=1024&h=1024',\n      caption: 'Photo by Blake Cheek',\n    }\n  ]\n}\nconst props2 = {\n  onClose: () => {},\n  imgs: [\n    {\n      src: 'https://images.unsplash.com/photo-1522931698295-e7b4d3e4188f?dpr=2&auto=format&w=1024&h=1024',\n      thumnail: 'https://images.unsplash.com/photo-1522931698295-e7b4d3e4188f?dpr=2&auto=format&crop=faces&fit=crop&w=300&h=300',\n    },\n    {\n      src: 'https://images.unsplash.com/photo-1482398650355-d4c6462afa0e?dpr=2&auto=format&w=1024&h=1024',\n      thumnail: 'https://images.unsplash.com/photo-1482398650355-d4c6462afa0e?dpr=2&auto=format&crop=faces&fit=crop&w=240&h=159',\n    }\n  ]\n}\nit('sum', () => {\n  expect(1 + 1).toBe(2)\n})\n// it('renders correctly',  () => {\n//   const component = mount(\n//     <ImgsViewer { ...props1 } />\n//   )\n//   component.setState({ isOpen: true })\n//   expect(component).toMatchSnapshot()\n// })\n// it('renders with thumbnail correctly',  () => {\n//   const component = mount(\n//     <ImgsViewer { ...props2 } />\n//   )\n//   component.setState({ isOpen: true })\n//   expect(component).toMatchSnapshot()\n// })\n// it('renders correctly after click',  () => {\n//   const component = mount(\n//     <ImgsViewer { ...props2 } />\n//   )\n//   component.setState({ isOpen: true })\n//   setTimeout(() => {\n//     component.find('figure').first() && component.find('figure').first().simulate('click')\n//     expect(component).toMatchSnapshot()\n//   }, 10000)\n// })\n// it('unmount',  () => {\n//   const component = mount(\n//     <ImgsViewer { ...props2 } />\n//   )\n//   component.setState({ isOpen: true })\n//   component.unmount()\n// })\n"
  },
  {
    "path": "__test__/jestsetup.js",
    "content": "import { configure } from 'enzyme'\nimport Adapter from 'enzyme-adapter-react-16'\n\nconfigure({ adapter: new Adapter() })\n"
  },
  {
    "path": "__test__/rafShim.js",
    "content": "global.requestAnimationFrame = callback => setTimeout(callback, 0)\n"
  },
  {
    "path": "__test__/utils.test.js",
    "content": "import { deepMerge, canUseDom, bindFunctions } from '../src/utils/util'\n\ndescribe('the deepMerge function', () => {\n  test('merge numb, stri in one level', () => expect(\n    deepMerge({ a: 1, b: 2 }, { a: '3', b: 4 })).toEqual({ a: '3', b: 4 })\n  )\n  test('replace str with arr', () => expect(\n    deepMerge({ a: 1 }, { a: ['3', 4] })).toEqual({ a: { 0: '3', 1: 4} })\n  )\n  test('replace arr with obj', () => expect(\n    deepMerge({ a: [1, 2] }, { a: { b: 1, c: '2' } })).toEqual({ a: { 0: 1, 1: 2, b: 1, c: '2' } })\n  )\n  test('replace arr with obj', () => expect(\n    deepMerge({ a: [1, 2] }, { a: { b: 1, c: '2' } })).toEqual({ a: { 0: 1, 1: 2, b: 1, c: '2' } })\n  )\n  test('replace obj with obj', () => expect(\n    deepMerge({ a: { b: 1 } }, { a: { b: '2' } })).toEqual({ a: { b: '2' } })\n  )\n  test('replace arr with arr', () => expect(\n    deepMerge({ a: [1] }, { a: { b: [2, 3] } })).toEqual({ a: { 0: 1, b: [2, 3] } })\n  )\n  test('replace obj with num', () => expect(\n    deepMerge({ a: 1 }, 2)).toEqual({ a: 1 })\n  )\n  test('replace num with num', () => expect(\n    deepMerge(1, 2)).toEqual({ })\n  )\n\n})\n"
  },
  {
    "path": "ci.sh",
    "content": "#!/bin/bash -e\n# daily push, but add all unStaged changes.\ncommit_msg=\"$1\"\ngit add .\ngit commit -m \"$commit_msg\"\ngit pull\ngit push\n"
  },
  {
    "path": "examples/src/.gitignore",
    "content": "## This file is here to ensure it is included in the gh-pages branch,\n## when 'gulp deploy' is used to push updates to the demo site.\n\n# Dependency directory\nnode_modules\n"
  },
  {
    "path": "examples/src/.npmignore",
    "content": ""
  },
  {
    "path": "examples/src/app.js",
    "content": "import React from \"react\";\nimport { render } from \"react-dom\";\nimport Gallery from \"./components/Gallery\";\nimport CustomSpinner from \"./components/Spinner\";\nimport \"./example.less\";\n\nfunction makeUnsplashSrc(id) {\n  return `https://images.unsplash.com/photo-${id}?dpr=2&auto=format&w=1024&h=1024`;\n}\nfunction makeUnsplashSrcSet(id, size) {\n  return `https://images.unsplash.com/photo-${id}?dpr=2&auto=format&w=${size} ${size}w`;\n}\nfunction makeUnsplashThumbnail(id, orientation = \"landscape\") {\n  const dimensions = orientation === \"square\" ? \"w=300&h=300\" : \"w=240&h=159\";\n\n  return `https://images.unsplash.com/photo-${id}?dpr=2&auto=format&crop=faces&fit=crop&${dimensions}`;\n}\n\n// Unsplash images from the \"Adventure\" collection\n// https://unsplash.com/collections/369/adventure\n\nconst DEFAULT_IMAGES = [\n  {\n    id: \"1526382551041-3c817fc3d478\",\n    caption: \"Photo by Simon Alexander\",\n    orientation: \"square\",\n    useForDemo: true,\n  },\n  {\n    id: \"1522985225914-17a10a58c8ec\",\n    caption: \"Photo by Blake Cheek\",\n    orientation: \"square\",\n    useForDemo: true,\n  },\n  {\n    id: \"1522931698295-e7b4d3e4188f\",\n    caption: \"Photo by Oliver Sjöström\",\n    orientation: \"square\",\n    useForDemo: true,\n  },\n  {\n    id: \"1516175663209-ac2459a5652f\",\n    caption: \"Photo by Jeremy Bishop\",\n    orientation: \"square\",\n    useForDemo: true,\n  },\n  {\n    id: \"1515911601378-97de98db6dda\",\n    caption: \"Photo by Emily Reider\",\n    orientation: \"square\",\n    useForDemo: true,\n  },\n];\nconst THEMED_IMAGES = [\n  {\n    id: \"1506773090264-ac0b07293a64\",\n    caption: \"Photo by Dan Grinwis\",\n    orientation: \"square\",\n    useForDemo: true,\n  },\n  {\n    id: \"1482398650355-d4c6462afa0e\",\n    caption: \"Photo by Andrew Neel\",\n    orientation: \"landscape\",\n    useForDemo: true,\n  },\n  {\n    id: \"1514949823529-bdcc933a9339\",\n    caption: \"Photo by Kristopher Roller\",\n    orientation: \"landscape\",\n    useForDemo: true,\n  },\n  {\n    id: \"1503293962593-47247718a17a\",\n    caption: \"Photo by Jeremy Bishop\",\n    orientation: \"landscape\",\n    useForDemo: true,\n  },\n  {\n    id: \"1509914398892-963f53e6e2f1\",\n    caption: \"Photo by Linus Nylund\",\n    orientation: \"landscape\",\n    useForDemo: true,\n  },\n];\nconst THUMBNAIL_IMAGES = [\n  {\n    id: \"1501963422762-3d89bd989568\",\n    caption: \"Photo by Jeremy Bishop\",\n    orientation: \"landscape\",\n    useForDemo: true,\n  },\n  {\n    id: \"1418846531910-2b7bb1043512\",\n    caption: \"Photo by Vincentiu Solomon\",\n    orientation: \"landscape\",\n    useForDemo: true,\n  },\n  {\n    id: \"1509529711801-deac231925ac\",\n    caption: \"Photo by Joshua Earle\",\n    orientation: \"landscape\",\n    useForDemo: true,\n  },\n  {\n    id: \"1499062229216-7c6349e898fb\",\n    caption: \"Photo by Leio McLaren\",\n    orientation: \"square\",\n    useForDemo: true,\n  },\n  {\n    id: \"1495619744764-2cc11fcbe5f0\",\n    caption: \"Photo by Philipp Kämmerer\",\n    orientation: \"square\",\n    useForDemo: true,\n  },\n  {\n    id: \"1488584433697-7ccc1148d30c\",\n    caption: \"Photo by Flecher Clay\",\n    orientation: \"square\",\n  },\n  {\n    id: \"1478562853135-c3c9e3ef7905\",\n    caption: \"Photo by Austin Neil\",\n    orientation: \"landscape\",\n  },\n  {\n    id: \"1476111021705-ac3b3304fe20\",\n    caption: \"Photo by Dino Reichmuth\",\n    orientation: \"square\",\n  },\n  {\n    id: \"1478001517127-fccc92f54906\",\n    caption: \"Photo by Joshua Earle\",\n    orientation: \"landscape\",\n  },\n  {\n    id: \"1455383333344-451b6147021b\",\n    caption: \"Photo by Joshua Earle\",\n    orientation: \"landscape\",\n  },\n];\n\nconst theme = {\n  // container\n  container: {\n    background: \"rgba(255, 255, 255, .9)\",\n  },\n\n  // arrows\n  arrow: {\n    backgroundColor: \"rgba(255, 255, 255, .8)\",\n    fill: \"#222\",\n    opacity: 0.6,\n    transition: \"opacity 200ms\",\n\n    \":hover\": {\n      opacity: 1,\n    },\n  },\n  arrow__size__medium: {\n    borderRadius: 40,\n    height: 40,\n    marginTop: -20,\n\n    \"@media (min-width: 768px)\": {\n      height: 70,\n      padding: 15,\n    },\n  },\n  arrow__direction__left: { marginLeft: 10 },\n  arrow__direction__right: { marginRight: 10 },\n  close: {\n    fill: \"#d40000\",\n    opacity: 0.6,\n    transition: \"all 200ms\",\n    \":hover\": {\n      opacity: 1,\n    },\n  },\n\n  // footer\n  footer: {\n    color: \"#000\",\n  },\n  footerCount: {\n    color: \"rgba(0, 0, 0, .6)\",\n  },\n\n  // thumbnails\n  thumbnail: {},\n  thumbnail__active: {\n    boxShadow: \"0 0 0 2px #00d8ff\",\n  },\n};\n// const theme = {\n//   container: {\n//     background: \"rgba(255, 255, 255, .9)\",\n//   },\n//   arrow: {\n//     backgroundColor: \"#222\",\n//     fill: \"#222\",\n//     opacity: 0.6,\n//     transition: \"opacity 200ms\",\n//     marginLeft: 300,\n//   },\n//   arrow__size__medium: {\n//     borderRadius: 40,\n//     height: 40,\n//     marginTop: -20,\n//     marginLeft: 300,\n//   },\n//   arrow__direction__left: { marginLeft: 300, background: \"blue\" },\n//   arrow__direction__right: { marginRight: 10, background: \"blue\" },\n//   close: {\n//     fill: \"#d40000\",\n//     opacity: 0.6,\n//     transition: \"all 200ms\",\n//   },\n//   footer: {\n//     color: \"#000\",\n//   },\n//   footerCount: {\n//     color: \"rgba(0, 0, 0, .6)\",\n//   },\n//   thumbnail: {\n//     activeBorderColor: \"#fff\",\n//     size: 50,\n//     gutter: 2,\n//   },\n//   thumbnail__active: {\n//     boxShadow: \"0 0 0 2px #00d8ff\",\n//   },\n// };\n\nrender(\n  <div>\n    <div>\n      <p>\n        Photos courtesy of{\" \"}\n        <a\n          href=\"https://unsplash.com/\"\n          target=\"_blank\"\n          rel=\"noopener noreferrer\"\n        >\n          Unsplash\n        </a>\n        . Use your keyboard to navigate <kbd>left, up</kbd>{\" \"}\n        <kbd>right, down</kbd> <kbd>space, esc</kbd> &mdash; Also, try resizing\n        your browser window.\n      </p>\n    </div>\n    <h3>Default Options</h3>\n    <Gallery\n      imgs={DEFAULT_IMAGES.map(({ caption, id, orientation, useForDemo }) => ({\n        src: makeUnsplashSrc(id),\n        thumbnail: makeUnsplashThumbnail(id, orientation),\n        srcSet: [\n          makeUnsplashSrcSet(id, 1024),\n          makeUnsplashSrcSet(id, 800),\n          makeUnsplashSrcSet(id, 500),\n          makeUnsplashSrcSet(id, 320),\n        ],\n        caption,\n        orientation,\n        useForDemo,\n      }))}\n    />\n\n    <h3>With Thumbnails</h3>\n    <Gallery\n      imgs={THUMBNAIL_IMAGES.map(\n        ({ caption, id, orientation, useForDemo }) => ({\n          src: makeUnsplashSrc(id),\n          thumbnail: makeUnsplashThumbnail(id, orientation),\n          srcSet: [\n            makeUnsplashSrcSet(id, 1024),\n            makeUnsplashSrcSet(id, 800),\n            makeUnsplashSrcSet(id, 500),\n            makeUnsplashSrcSet(id, 320),\n          ],\n          caption,\n          orientation,\n          useForDemo,\n        })\n      )}\n      showThumbnails\n    />\n\n    <h3>Themed ImgsViewer</h3>\n    <Gallery\n      imgs={THEMED_IMAGES.map(({ caption, id, orientation, useForDemo }) => ({\n        src: makeUnsplashSrc(id),\n        thumbnail: makeUnsplashThumbnail(id, orientation),\n        srcSet: [\n          makeUnsplashSrcSet(id, 1024),\n          makeUnsplashSrcSet(id, 800),\n          makeUnsplashSrcSet(id, 500),\n          makeUnsplashSrcSet(id, 320),\n        ],\n        caption,\n        orientation,\n        useForDemo,\n      }))}\n      theme={theme}\n      spinner={CustomSpinner}\n      spinnerColor={\"#d40000\"}\n      spinnerSize={150}\n      showThumbnails\n    />\n  </div>,\n  document.getElementById(\"example\")\n);\n"
  },
  {
    "path": "examples/src/app_CN.js",
    "content": "import React from 'react'\nimport { render } from 'react-dom'\nimport Gallery from './components/Gallery'\nimport CustomSpinner from './components/Spinner'\nimport './example.less'\n\nfunction makeUnsplashSrc (id) {\n  return `https://images.unsplash.com/photo-${id}?dpr=2&auto=format&w=1024&h=1024`\n}\nfunction makeUnsplashSrcSet(id, size) {\n  return `https://images.unsplash.com/photo-${id}?dpr=2&auto=format&w=${size} ${size}w`\n}\nfunction makeUnsplashThumbnail (id, orientation = 'landscape') {\n  const dimensions = orientation === 'square' ?\n    'w=300&h=300' :\n    'w=240&h=159'\n\n  return `https://images.unsplash.com/photo-${id}?dpr=2&auto=format&crop=faces&fit=crop&${dimensions}`\n}\n\n// Unsplash images from the \"Adventure\" collection\n// https://unsplash.com/collections/369/adventure\n\nconst DEFAULT_IMAGES = [\n  { id: '1526382551041-3c817fc3d478', caption: '照片来自Simon Alexander', orientation: 'square', useForDemo: true },\n  { id: '1522985225914-17a10a58c8ec', caption: '照片来自Blake Cheek', orientation: 'square', useForDemo: true },\n  { id: '1522931698295-e7b4d3e4188f', caption: '照片来自Oliver Sjöström', orientation: 'square', useForDemo: true },\n  { id: '1516175663209-ac2459a5652f', caption: '照片来自Jeremy Bishop', orientation: 'square', useForDemo: true },\n  { id: '1515911601378-97de98db6dda', caption: '照片来自Emily Reider', orientation: 'square', useForDemo: true },\n]\nconst THEMED_IMAGES = [\n  { id: '1506773090264-ac0b07293a64', caption: '照片来自Dan Grinwis', orientation: 'square', useForDemo: true },\n  { id: '1482398650355-d4c6462afa0e', caption: '照片来自Andrew Neel', orientation: 'landscape', useForDemo: true },\n  { id: '1514949823529-bdcc933a9339', caption: '照片来自Kristopher Roller', orientation: 'landscape', useForDemo: true },\n  { id: '1503293962593-47247718a17a', caption: '照片来自Jeremy Bishop', orientation: 'landscape', useForDemo: true },\n  { id: '1509914398892-963f53e6e2f1', caption: '照片来自Linus Nylund', orientation: 'landscape', useForDemo: true },\n]\nconst THUMBNAIL_IMAGES = [\n  { id: '1501963422762-3d89bd989568', caption: '照片来自Jeremy Bishop', orientation: 'landscape', useForDemo: true },\n  { id: '1418846531910-2b7bb1043512', caption: '照片来自Vincentiu Solomon', orientation: 'landscape', useForDemo: true },\n  { id: '1509529711801-deac231925ac', caption: '照片来自Joshua Earle', orientation: 'landscape', useForDemo: true },\n  { id: '1499062229216-7c6349e898fb', caption: '照片来自Leio McLaren', orientation: 'square', useForDemo: true },\n  { id: '1495619744764-2cc11fcbe5f0', caption: '照片来自Philipp Kämmerer', orientation: 'square', useForDemo: true },\n  { id: '1488584433697-7ccc1148d30c', caption: '照片来自Flecher Clay', orientation: 'square' },\n  { id: '1478562853135-c3c9e3ef7905', caption: '照片来自Austin Neil', orientation: 'landscape' },\n  { id: '1476111021705-ac3b3304fe20', caption: '照片来自Dino Reichmuth', orientation: 'square' },\n  { id: '1478001517127-fccc92f54906', caption: '照片来自Joshua Earle', orientation: 'landscape' },\n  { id: '1455383333344-451b6147021b', caption: '照片来自Joshua Earle', orientation: 'landscape' },\n]\n\nconst theme = {\n  // container\n  container: {\n    background: 'rgba(255, 255, 255, .9)'\n  },\n\n  // arrows\n  arrow: {\n    backgroundColor: 'rgba(255, 255, 255, .8)',\n    fill: '#222',\n    opacity: .6,\n    transition: 'opacity 200ms',\n\n    ':hover': {\n      opacity: 1,\n    }\n  },\n  arrow__size__medium: {\n    borderRadius: 40,\n    height: 40,\n    marginTop: -20,\n\n    '@media (min-width: 768px)': {\n      height: 70,\n      padding: 15,\n    }\n  },\n  arrow__direction__left: { marginLeft: 10 },\n  arrow__direction__right: { marginRight: 10 },\n  close: {\n    fill: '#d40000',\n    opacity: .6,\n    transition: 'all 200ms',\n    ':hover': {\n      opacity: 1\n    }\n  },\n\n  // footer\n  footer: {\n    color: '#000'\n  },\n  footerCount: {\n    color: 'rgba(0, 0, 0, .6)'\n  },\n\n  // thumbnails\n  thumbnail:{\n\n  },\n  thumbnail__active: {\n    boxShadow: '0 0 0 2px #00d8ff'\n  }\n};\n\nrender(\n  <div>\n    <div>\n      <p>照片均来自\n        <a href=\"https://unsplash.com/\" target=\"_blank\" rel=\"noopener noreferrer\">Unsplash</a>.\n        采用以下键盘按钮来导航：\n        <kbd>向上，向左（上一张）</kbd> <kbd>向下，向右（下一张）</kbd> <kbd>空格，Esc（关闭）</kbd> &mdash;\n        你也可以尝试改变一下浏览器窗口大小~\n      </p>\n    </div>\n    <h3>默认选项</h3>\n    <Gallery\n      imgs={DEFAULT_IMAGES.map(({ caption, id, orientation, useForDemo }) => ({\n        src: makeUnsplashSrc(id),\n        thumbnail: makeUnsplashThumbnail(id, orientation),\n        srcSet: [\n          makeUnsplashSrcSet(id, 1024),\n          makeUnsplashSrcSet(id, 800),\n          makeUnsplashSrcSet(id, 500),\n          makeUnsplashSrcSet(id, 320)\n        ],\n        caption,\n        orientation,\n        useForDemo,\n      }))}\n    />\n\n    <h3>缩略图同步</h3>\n    <Gallery\n      imgs={THUMBNAIL_IMAGES.map(({ caption, id, orientation, useForDemo }) => ({\n        src: makeUnsplashSrc(id),\n        thumbnail: makeUnsplashThumbnail(id, orientation),\n        srcSet: [\n          makeUnsplashSrcSet(id, 1024),\n          makeUnsplashSrcSet(id, 800),\n          makeUnsplashSrcSet(id, 500),\n          makeUnsplashSrcSet(id, 320)\n        ],\n        caption,\n        orientation,\n        useForDemo,\n      }))}\n      showThumbnails\n    />\n\n    <h3>主题可定制</h3>\n    <Gallery\n      imgs={THEMED_IMAGES.map(({ caption, id, orientation, useForDemo }) => ({\n        src: makeUnsplashSrc(id),\n        thumbnail: makeUnsplashThumbnail(id, orientation),\n        srcSet: [\n          makeUnsplashSrcSet(id, 1024),\n          makeUnsplashSrcSet(id, 800),\n          makeUnsplashSrcSet(id, 500),\n          makeUnsplashSrcSet(id, 320)\n        ],\n        caption,\n        orientation,\n        useForDemo,\n      }))}\n      theme={theme}\n      spinner={CustomSpinner}\n      spinnerColor={'#d40000'}\n      spinnerSize={150}\n      showThumbnails\n    />\n  </div>,\n  document.getElementById('example-CN')\n)\n"
  },
  {
    "path": "examples/src/components/DownloadButton/icon.js",
    "content": "module.exports = (\n    '<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" version=\"1.1\" x=\"0px\" y=\"0px\" viewBox=\"0 0 24 30\" enable-background=\"new 0 0 24 24\" xml:space=\"preserve\">'\n        + '<g>'\n            + '<polygon fill=\"white\" points=\"19,14 19,18 5,18 5,14 2,14 2,18 2,22 5,22 19,22 22,22 22,18 22,14  \"/>'\n            + '<polygon fill=\"white\" points=\"15,9.9 15,2 9,2 9,9.9 5.8,9.9 12,16.1 18.2,9.9  \"/>'\n        + '</g>'\n    + '</svg>'\n);\n"
  },
  {
    "path": "examples/src/components/DownloadButton/index.js",
    "content": "import PropTypes from 'prop-types'\nimport React, { Component } from 'react'\nimport DownloadIcon from './icon'\n\nclass DownloadButton extends Component {\n  constructor () {\n    super()\n  }\n  render () {\n    return (\n      <button\n        title=\"Download\"\n        style={{\n          background: 'none',\n          border: 'none',\n          cursor: 'pointer',\n          float: 'left',\n          height: 40,\n          outline: 'none',\n          padding: 10,\n          position: 'relative',\n          left: -10,\n          top: 0,\n          verticalAlign: 'bottom',\n          width: 40,\n        }}\n        onClick={this.props.handler}\n      >\n        <span dangerouslySetInnerHTML={{ __html: DownloadIcon }} />\n      </button>\n    )\n  }\n}\n\nexport default DownloadButton\n"
  },
  {
    "path": "examples/src/components/Gallery.js",
    "content": "import React, { Component } from 'react'\nimport ImgsViewer from 'react-images-viewer'\nimport PropTypes from 'prop-types'\nimport { css, StyleSheet } from 'aphrodite/no-important'\n\nclass Gallery extends Component {\n  constructor () {\n    super()\n\n    this.state = {\n      isOpen: false,\n      currImg: 0,\n    }\n\n    this.gotoNext = this.gotoNext.bind(this)\n    this.gotoPrev = this.gotoPrev.bind(this)\n    this.gotoImg = this.gotoImg.bind(this)\n    this.handleClickImg = this.handleClickImg.bind(this)\n    this.closeImgsViewer = this.closeImgsViewer.bind(this)\n    this.openImgsViewer = this.openImgsViewer.bind(this)\n  }\n  openImgsViewer (index, event) {\n    event.preventDefault()\n    this.setState({\n      currImg: index,\n      isOpen: true,\n    })\n  }\n  closeImgsViewer () {\n    this.setState({\n      currImg: 0,\n      isOpen: false,\n    })\n  }\n  gotoPrev () {\n    this.setState({\n      currImg: this.state.currImg - 1\n    })\n  }\n  gotoNext () {\n    this.setState({\n      currImg: this.state.currImg + 1\n    })\n  }\n  gotoImg (index) {\n    this.setState({\n      currImg: index\n    })\n  }\n  handleClickImg () {\n    if (this.state.currImg === this.props.imgs.length - 1) return\n    this.gotoNext()\n  }\n  renderGallery () {\n    const { imgs } = this.props\n\n    if (!imgs) return\n\n    const gallery = imgs.filter(i => i.useForDemo).map((obj, i) => {\n      return (\n        <a\n          href={obj.src}\n          className={css(classes.thumbnail, classes[obj.orientation])}\n          key={i}\n          onClick={(e) => this.openImgsViewer(i, e)}\n        >\n          <img src={obj.thumbnail} className={css(classes.source)} />\n        </a>\n      )\n    })\n\n    return (\n      <div className={css(classes.gallery)}>\n        {gallery}\n      </div>\n    )\n  }\n  render () {\n    return (\n      <div>\n        {this.props.heading && <h2>{this.props.heading}</h2>}\n        {this.props.subheading && <p>{this.props.subheading}</p>}\n        {this.renderGallery()}\n        <ImgsViewer\n          backdropCloseable\n          currImg={this.state.currImg}\n          imgs={this.props.imgs}\n          isOpen={this.state.isOpen}\n          onClickImg={this.handleClickImg}\n          onClickNext={this.gotoNext}\n          onClickPrev={this.gotoPrev}\n          onClickThumbnail={this.gotoImg}\n          onClose={this.closeImgsViewer}\n          preventScroll={this.props.preventScroll}\n          showThumbnails={this.props.showThumbnails}\n          spinner={this.props.spinner}\n          spinnerColor={this.props.spinnerColor}\n          spinnerSize={this.props.spinnerSize}\n          theme={this.props.theme}\n        />\n      </div>\n    )\n  }\n}\n\nGallery.displayName = 'Gallery'\nGallery.propTypes = {\n  preventScroll: PropTypes.bool,\n  spinner: PropTypes.func,\n  spinnerColor: PropTypes.string,\n  spinnerSize: PropTypes.number,\n  theme: PropTypes.object,\n  heading: PropTypes.string,\n  imgs: PropTypes.array,\n  showThumbnails: PropTypes.bool,\n  subheading: PropTypes.string,\n}\n\nconst gutter = {\n  small: 2,\n  large: 4,\n}\nconst classes = StyleSheet.create({\n  gallery: {\n    marginRight: -gutter.small,\n    overflow: 'hidden',\n    '@media (min-width: 500px)': {\n      marginRight: -gutter.large,\n    }\n  },\n\n  // anchor\n  thumbnail: {\n    boxSizing: 'border-box',\n    display: 'block',\n    float: 'left',\n    lineHeight: 0,\n    paddingRight: gutter.small,\n    paddingBottom: gutter.small,\n    overflow: 'hidden',\n\n    '@media (min-width: 500px)': {\n      paddingRight: gutter.large,\n      paddingBottom: gutter.large,\n    }\n  },\n\n  // orientation\n  landscape: {\n    width: '30%',\n  },\n  square: {\n    paddingBottom: gutter.large,\n    width: '40%',\n\n    '@media (min-width: 500px)': {\n      paddingBottom: gutter.large,\n    }\n  },\n\n  // actual <img />\n  source: {\n    border: 0,\n    display: 'block',\n    height: 'auto',\n    maxWidth: '100%',\n    width: 'auto'\n  },\n})\n\nexport default Gallery\n"
  },
  {
    "path": "examples/src/components/Spinner.js",
    "content": "import PropTypes from 'prop-types'\nimport React from 'react'\nimport { css, StyleSheet } from 'aphrodite/no-important'\n\nconst Spinner = (props) => {\n  const classes = StyleSheet.create(styles(props))\n\n  return (\n    <div className={css(classes.spinner)}>\n      <div className={css(classes.square)} />\n    </div>\n  )\n}\n\nSpinner.propTypes = {\n  color: PropTypes.string,\n  size: PropTypes.number,\n}\n\nconst squareKeyframes = {\n  '0%': {\n    top: 0,\n    left: '25%',\n    opacity: 1,\n  },\n  '25%': {\n    top: '50%',\n    left: '50%',\n    opacity: .75,\n  },\n  '75%': {\n    top: '50%',\n    left: 0,\n    opacity: .5,\n  },\n  '100%': {\n    top: 0,\n    left: '25%',\n    opacity: 1,\n  }\n}\n\nconst styles = ({ color, size }) => ({\n  spinner: {\n    display: 'inline-block',\n    position: 'relative',\n    width: size,\n    height: size,\n  },\n  square: {\n    position: 'absolute',\n    width: size / 10,\n    height: size / 10,\n    border: `4px solid ${color}`,\n    borderRadius: '50%',\n    background: color,\n    animationName: squareKeyframes,\n    animationDuration: '2s',\n    animationTimingFunction: 'linear',\n    animationIterationCount: 'infinite',\n  }\n})\n\nexport default Spinner\n"
  },
  {
    "path": "examples/src/example.less",
    "content": "// Cmomon Example Styles\n\n\n\n// Constans\n// example site\n@gutter:\t\t\t\t\t\t\t20px;\n@table-cell-gutter: \t10px;\n@left-col-width:      180px;\n@heading-color:     #000;\n\n@kbd-bg-color:\t\t\t#fafafa;\n@kbd-border-color:\t#ccc;\n\n@link-color:        #00d7ff;\n@text-color:        #333;\n\n@nav-gutter:\t\t\t\t\t30px;\n@nav-item-padding: 5px;\n@nav-padding: @nav-gutter - @nav-item-padding;\n\n\n// Base\nbody {\n\tbackground-color: '#fff';\n  color: @text-color;\n  font-family: 'Misrosoft Yahei', Helvetica Neue, Helvetica, Arial, sans-serif;\n  font-size: 14px;\n  line-height: 1.4;\n  margin: 0;\n  padding: 0;\n}\n\na {\n  color: @link-color;\n  text-decoration: none;\n  &:hover {\n    text-decoration: underline;\n  }\n}\nhr {\n\tborder: none;\n\tborder-top: 1px solid rgba(0, 0, 0, .1);\n\tmargin-top: 2em;\n\tmargin-bottom: 2em;\n}\n\n.container {\n  margin-right: auto;\n  margin-left: auto;\n  max-width: 940px;\n  padding: 0 @gutter;\n}\n\n// Columns\n.left-col {\n\tdisplay: none;\n\tfloat: left;\n\tposition: fixed;\n\twidth: @left-col-width;\n\tz-index: 1;\n}\n\n// Navigation\n.page-nav {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0 @nav-padding 0 0;\n\ttext-align: right;\n}\n.page-nav__item {\n\n}\n.page-nav__link {\n\tdisplay: block;\n\tpadding: @nav-item-padding;\n}\n\n// Headings\nh1, h2, h3, h4, h5, h6,\n.h1, .h2, .h3, .h4, .h5, .h6 {\n  color: @heading-color;\n  font-weight: 500;\n  line-height: 1;\n  margin-bottom: 0.66em;\n  margin-top: 0;\n}\n\nh1, .h1 {\n  font-size: 3em;\n}\nh2, .h2 {\n  font-size: 2em;\n\tfont-weight: 300;\n\tpadding-top: @nav-gutter;\n}\nh3, .h3 {\n\tcolor: #999;\n\tfont-size: 1em;\n\tfont-weight: bold;\n\tmargin-top: 2em;\n\ttext-transform: uppercase;\n}\nh4, .h4 {\n  font-size: 1em;\n}\nh5, .h5 {\n  font-size: .85em;\n}\nh6, .h6 {\n  font-size: .75em;\n}\n\n// Layout\n// ------------------------------\n\n// header\n\n.page-header {\n\tpadding: @gutter 0;\n\n\tp {\n\t\tmargin: 0;\n\t}\n\ta {\n\t\tborder-bottom: 1px solid fade(white, 30%);\n\t\ttext-decoration: none;\n\n\t\t&:hover,\n\t\t&:focus {\n\t\t\tborder-bottom-color: white;\n\t\t\toutline: none;\n\t\t\ttext-decoration: none;\n\t\t}\n\t}\n}\n.page-header__title {\n\tfont-style: 3em;\n}\n.page-header__subtitle {\n\tcolor: #777777;\n\tfont-size: 1.125em;\n\tfont-weight: 300;\n}\n\n// subheader\n\n.page-subheader {\n\tbackground-color: mix(@link-color, white, 8%);\n\tborder-radius: 4px;\n\tpadding: @gutter;\n}\n.page-subheader__button {\n\tfloat: right;\n}\n.page-subheader__link {\n\tborder-bottom: 1px solid fade(@link-color, 30%);\n\toutline: none;\n\ttext-decoration: none;\n\n\t&:hover,\n\t&:focus {\n\t\tborder-bottom-color: @link-color;\n\t\toutline: none;\n\t\ttext-decoration: none;\n\t}\n}\n\n// content\n\n.page-content {\n\tpadding-bottom: @nav-gutter;\n}\n\n// footer\n\n.page-footer {\n\tbackground-color: #fafafa;\n\tcolor: #999;\n\tpadding: @nav-gutter 0;\n\ttext-align: center;\n}\n\n// options table\n\n.options-table {\n\tborder-collapse: collapse;\n\tborder-spacing: 0;\n\tmargin-left: -@table-cell-gutter;\n\tmargin-right: -@table-cell-gutter;\n\tmin-width: 100%;\n\twidth: 100%;\n\ttd, th {\n\t\tpadding: @table-cell-gutter;\n\t}\n\tth {\n\t\tborder-bottom: 1px solid rgba(0, 0, 0, .05);\n\t\tcolor: #999999;\n\t\tfont-weight: normal;\n\t}\n\ttd {\n\t\tborder-top: 1px solid rgba(0, 0, 0, .05);\n\t}\n}\n\n// Miscellaneous\n// --------------------\n\n.section {\n\tmargin-bottom: 40px;\n}\n\n.hint {\n\tfont-size: .85em;\n\tmargin: 15px 0;\n\tcolor: #666;\n}\n\nkbd {\n\tbackground-color: @kbd-bg-color;\n\tborder-radius: 3px;\n\tborder: 1px solid @kbd-border-color;\n\tborder-bottom-color: darken(@kbd-border-color, 4%);\n\tborder-top-color: lighten(@kbd-border-color, 4%);\n\tbox-shadow: 0 1px 1px rgba(0, 0, 0, .12), 0 2px 0 0 rgba(255, 255, 255, .7) inset;\n\tcolor: #666666;\n\tdisplay: inline-block;\n\tfont-family: 'Microsoft Yahei';\n\tfont-size: .85em;\n\tfont-weight: 500;\n\tline-height: inherit;\n\tpadding: 1px 4px;\n\twhite-space: nowrap;\n\n\t// little hack to tweak 'visual-middle' alignment\n\tposition: relative;\n\ttop: -1px;\n}\n\npre {\n\tbackground-color: #eeeeee;\n\tborder-radius: 4px;\n\tmargin-bottom: @nav-gutter;\n}\n\n\n// Responsive\n// --------------------\n\n// largee phones and up\n\n@media (min-width: 481px) {\n\t.page-header,\n\t.page-nav {\n\t\tpadding-bottom: @nav-gutter;\n\t\tpadding-top: @nav-gutter;\n\t}\n\t.page-header__title\t{\n\t\tfont-size: 4em;\n\t}\n\t.page-subheader {\n\t\tfont-size: 1.125em;\n\t\tline-height: 28px;\n\t}\n}\n\n// large phones and down\n\n@media (max-width: 768px) {\n\t.options-table-container {\n\t\toverflow-x: scroll;\n\t\toverflow-y: hidden;\n\t\twidth: 100%;\n\t\t-ms-overflow-style: -ms-autohiding-scrollbar;\n\t\t-webkit-overflow-scrolling: touch;\n\t}\n\t.options-table {\n\t\ttable-layout: auto;\n\n\t\ttd {\n\t\t\twhite-space: nowrap;\n\t\t}\n\t}\n}\n\n// tablets and up\n\n@media (min-width: 769px) {\n\t.left-col {\n\t\tdisplay: block;\n\t}\n\t.right-col {\n\t\tpadding-left: @left-col-width;\n\t}\n\t.page-content {\n\t\tpadding-left: @nav-gutter;\n\t\tposition: relative;\n\n\t\t&::before {\n\t\t\tbackground: linear-gradient(90deg, fade(#e9e9e9, 0%) 94%, #e9e9e9), linear-gradient(90deg, fade(#f6f6f6, 0%) 50%, #f6f6f6);\n\t\t\tborder-right: 1px solid #e6e6e6;\n\t\t\tcontent: \" \";\n\t\t\tmargin-left: -(@left-col-width + @nav-gutter);\n\t\t\theight: 100%;\n\t\t\tposition: fixed;\n\t\t\twidth: @left-col-width;\n\t\t}\n\t}\n\t.page-header__subtitle {\n\t\tfont-size: 2em;\n\t}\n\t.page-footer {\n\t\tborder: none;\n\t\tbottom: 0;\n\t\tfloat: left;\n\t\tmargin-left: -@nav-gutter;\n\t\tposition: fixed;\n\t\ttext-align: right;\n\t\twidth: @left-col-width;\n\t\tz-index: 1;\n\t}\n\t.page-footer__copyright--small {\n\t\tdisplay: none;\n\t}\n}\n"
  },
  {
    "path": "examples/src/index.html",
    "content": "<!doctype html>\n<head>\n  <meta charset=\"UTF-8\">\n  <title>React-Images-Viewer</title>\n  <meta name=\"viewport\" content=\"width=device-width, user-scalable=no, maximum-initial-scale=1.0, initial-scale=1.0\">\n  <meta name=\"keywords\" content=\"react,reactjs,react component,imgsViewer,react images,react-images,react images viewer,react photos,react photos list,react-images-viewer,ui,javascript\">\n  <meta name=\"description\" content=\"A react library that view photos list easily, and a simple, responsive imgsViewer component for display an array of images\">\n  <meta name=\"og:locale\" content=\"zh-cn\">\n  <meta name=\"og:title\" content=\"React-Images-Viewer\">\n  <meta name=\"og:description\" content=\"A react library that view photos list easily, and a simple, responsive imgsViewer component for display an array of images\">\n  <meta name=\"og:url\" content=\"https://guonanci.github.io/react-images-viewer\">\n  <meta name=\"og:site_name\" content=\"React-Images-Viewer\">\n  <meta name=\"og:type\" content=\"article\">\n  <link rel=\"stylesheet\" href=\"example.css\">\n  <link rel=\"icon\" type=\"image/x-icon\" href=\"favicon.ico\">\n</head>\n<body>\n  <div class=\"page-wrapper\">\n    <div class=\"container\">\n      <nav class=\"left-col\">\n        <ul class=\"page-nav\">\n          <li class=\"page-nav__item\">\n            <a href=\"#example\" class=\"page-nav__link\">Examples</a>\n          </li>\n          <li class=\"page-nav__item\">\n            <a href=\"#getting-started\" class=\"page-nav__link\">Getting started</a>\n          </li>\n          <li class=\"page-nav__item\">\n            <a href=\"#options\" class=\"page-nav__link\">Options</a>\n          </li>\n          <li class=\"page-nav__item\">\n            <a href=\"#license\" class=\"page-nav__link\">License</a>\n          </li>\n          <li class=\"page-nav__item\">\n            <a href=\"#help\" class=\"page-nav__link\">Help</a>\n          </li>\n          <li class=\"page-nav__item\">\n            <a href=\"./index_CN.html\" class=\"page-nav__link\">中文</a>\n          </li>\n        </ul>\n      </nav>\n      <div class=\"right-col\">\n        <div class=\"page-content\">\n          <header class=\"page-header\">\n            <h1 class=\"page-header__title\">React Images Viewer</h1>\n            <p class=\"page-header__subtitle\">A simple, responsive imgsViewer component for <a href=\"http://facebook.github.io/react/\" target=\"_blank\">ReactJS</a> to display an array of images.</p>\n          </header>\n          <div class=\"page-subheader\">\n            <a href=\"https://github.com/guonanci/react-images-viewer\" class=\"page-subheader__link\" target=\"_blank\">Code and Docs on GitHub</a>\n            <span class=\"page-subheader__button\">\n              <a href=\"https://github.com/guonanci/react-images-viewer\" id=\"github-stars-button\" class=\"github-button\" data-style=\"\" data-count-href=\"/guonanci/react-images-viewer/stargazers\" data-count-api=\"/repos/\">Star</a>\n            </span>\n          </div>\n          <div class=\"page-body\">\n            <section id=\"examples\" class=\"section-examples\">\n              <h2>Examples</h2>\n              <div id=\"example\"></div>\n            </section>\n            <section id=\"getting-started\" class=\"section-getting-started\">\n              <h2>Getting Started</h2>\n              <p>1. Install using yarn:</p>\n              <pre>yarn add react-images-viewer</pre>\n              <p>… or npm:</p>\n              <pre>npm install react-images-viewer --save</pre>\n              <p>2. Example usage with JSX:</p>\n              <pre>\n                <code>\n  &lt;ImgsViewer\n    imgs={[\n      { src: '../images/photo-1.jpg' },\n      { src: '../images/photo-2.jpg' }\n    ]}\n    isOpen={this.state.isOpen}\n    onClickPrev={this.gotoPrevImg}\n    onClickNext={this.gotoNextImg}\n    onClose={this.closeImgsViewer}\n  /&gt;\n                </code>\n              </pre>\n            </section>\n            <section id=\"options\" class=\"section-options\">\n              <h2>Options</h2>\n              <div class=\"options-table-container\">\n                <table class=\"options-table\">\n                  <thead>\n                    <tr>\n                      <th align=\"left\">Property</th>\n                      <th align=\"left\">Type</th>\n                      <th align=\"left\">Default</th>\n                      <th align=\"left\">Description</th>\n                    </tr>\n                  </thead>\n                  <tbody>\n                    <tr>\n                      <td align=\"left\">backdropCloseable</td>\n                      <td align=\"left\">bool</td>\n                      <td align=\"left\">false</td>\n                      <td align=\"left\">Allow users to exit the imgsViewer by clicking the backdrop</td>\n                    </tr>\n                    <tr>\n                      <td align=\"left\">currImg</td>\n                      <td align=\"left\">number</td>\n                      <td align=\"left\">0</td>\n                      <td align=\"left\">The index of the image to display initially</td>\n                    </tr>\n                    <tr>\n                      <td align=\"left\">customControls</td>\n                      <td align=\"left\">array</td>\n                      <td align=\"left\">undefined</td>\n                      <td align=\"left\">An array of elements to display as custom controls on the top of imgsViewer</td>\n                    </tr>\n                    <tr>\n                      <td align=\"left\">enableKeyboardInput</td>\n                      <td align=\"left\">bool</td>\n                      <td align=\"left\">true</td>\n                      <td align=\"left\">Supports keyboard input -\n                        <code>space, esc</code>,\n                        <code>arrow left, arrow up</code>, and\n                        <code>arrow right, arrow down</code>\n                      </td>\n                    </tr>\n                    <tr>\n                      <td align=\"left\">\n                        <a href=\"#images\">imgs</a>\n                      </td>\n                      <td align=\"left\">array</td>\n                      <td align=\"left\">undefined</td>\n                      <td align=\"left\">Required. An array of objects containing valid src and srcset values of img element</td>\n                    </tr>\n                    <tr>\n                      <td align=\"left\">imgCountSeparator</td>\n                      <td align=\"left\">string</td>\n                      <td align=\"left\">' / '</td>\n                      <td align=\"left\">Custom separator for the image count</td>\n                    </tr>\n                    <tr>\n                      <td align=\"left\">isOpen</td>\n                      <td align=\"left\">bool</td>\n                      <td align=\"left\">false</td>\n                      <td align=\"left\">Whether or not the imgsViewer is displayed</td>\n                    </tr>\n                    <tr>\n                      <td align=\"left\">onClickPrev</td>\n                      <td align=\"left\">func</td>\n                      <td align=\"left\">undefined</td>\n                      <td align=\"left\">Fired on request of the previous image</td>\n                    </tr>\n                    <tr>\n                      <td align=\"left\">onClickNext</td>\n                      <td align=\"left\">func</td>\n                      <td align=\"left\">undefined</td>\n                      <td align=\"left\">Fired on request of the next image</td>\n                    </tr>\n                    <tr>\n                      <td align=\"left\">onClickImg</td>\n                      <td align=\"left\">func</td>\n                      <td align=\"left\">undefined</td>\n                      <td align=\"left\">Handle click event on the current image</td>\n                    </tr>\n                    <tr>\n                      <td align=\"left\">onClickThumbnail</td>\n                      <td align=\"left\">func</td>\n                      <td align=\"left\">undefined</td>\n                      <td align=\"left\">Handle click on thumbnail</td>\n                    </tr>\n                    <tr>\n                      <td align=\"left\">onClose</td>\n                      <td align=\"left\">func</td>\n                      <td align=\"left\">undefined</td>\n                      <td align=\"left\">Required. Handle closing of the imgsViewer</td>\n                    </tr>\n                    <tr>\n                      <td align=\"left\">preloadNextImg</td>\n                      <td align=\"left\">bool</td>\n                      <td align=\"left\">true</td>\n                      <td align=\"left\">Whether to preload the next available image.</td>\n                    </tr>\n                    <tr>\n                      <td align=\"left\">preventAutoScroll</td>\n                      <td align=\"left\">bool</td>\n                      <td align=\"left\">true</td>\n                      <td align=\"left\">Determines whether auto-scrolling is prevented</td>\n                    </tr>\n                    <tr>\n                      <td align=\"left\">showCloseBtn</td>\n                      <td align=\"left\">bool</td>\n                      <td align=\"left\">true</td>\n                      <td align=\"left\">Optionally display a close \"X\" button in top right corner</td>\n                    </tr>\n                    <tr>\n                      <td align=\"left\">showImgCount</td>\n                      <td align=\"left\">bool</td>\n                      <td align=\"left\">true</td>\n                      <td align=\"left\">Optionally display image index, e.g., \"3 of 20\"</td>\n                    </tr>\n                    <tr>\n                      <td align=\"left\">showThumbnails</td>\n                      <td align=\"left\">bool</td>\n                      <td align=\"left\">false</td>\n                      <td align=\"left\">Optionally display thumbnails beneath the imgsViewer</td>\n                    </tr>\n                    <tr>\n                      <td align=\"left\">theme</td>\n                      <td align=\"left\">object</td>\n                      <td align=\"left\">undefined</td>\n                      <td align=\"left\">Pass through styles to theme each component;\n                        <code>arrow</code>,\n                        <code>container</code>, etc.</td>\n                    </tr>\n                    <tr>\n                      <td align=\"left\">width</td>\n                      <td align=\"left\">number</td>\n                      <td align=\"left\">1024</td>\n                      <td align=\"left\">Maximum width of the carousel; defaults to 1024px</td>\n                    </tr>\n                  </tbody>\n                </table>\n              </div>\n              <h2>Images</h2>\n              <div class=\"options-table-container\" id=\"images\">\n                <table class=\"options-table\">\n                  <thead>\n                    <tr>\n                      <th align=\"left\">Property</th>\n                      <th align=\"left\">Type</th>\n                      <th align=\"left\">Default</th>\n                      <th align=\"left\">Description</th>\n                    </tr>\n                  </thead>\n                  <tbody>\n                    <tr>\n                      <td align=\"left\">src</td>\n                      <td align=\"left\">str</td>\n                      <td align=\"left\">undefined</td>\n                      <td align=\"left\">Required. The primary image path</td>\n                    </tr>\n                    <tr>\n                      <td align=\"left\">srcset</td>\n                      <td align=\"left\">arr</td>\n                      <td align=\"left\">undefined</td>\n                      <td align=\"left\">List of alternative image sizes</td>\n                    </tr>\n                    <tr>\n                      <td align=\"left\">caption</td>\n                      <td align=\"left\">str</td>\n                      <td align=\"left\">undefined</td>\n                      <td align=\"left\">Displayed benath the current image. Great for description or attribution</td>\n                    </tr>\n                    <tr>\n                      <td align=\"left\">thumbnail</td>\n                      <td align=\"left\">str</td>\n                      <td align=\"left\">undefined</td>\n                      <td align=\"left\">Thumbnail to display if\n                        <code>showThumbnails</code> is set to\n                        <code>true</code>\n                      </td>\n                    </tr>\n                  </tbody>\n                </table>\n              </div>\n            </section>\n\n            <section id=\"license\" class=\"section-license\">\n              <h2>License</h2>\n              <p>React Images Viewer is free to use for personal and commercial projects under\n                <a target=\"_blank\" href=\"https://github.com/guonanci/react-images-viewer/blob/master/LICENSE\">the MIT license</a>.</p>\n              <p>Attribution is not required, but greatly appreciated. It does not have to be user-facing and can remain within the code.</p>\n            </section>\n\n            <section id=\"help\" class=\"section-help\">\n              <h2>Help</h2>\n\n              <h3>Have a question?</h3>\n              <p>Follow the\n                <a target=\"_blank\" href=\"https://github.com/guonanci/react-images-viewer#quick-start\">quick start guide</a> on GitHub to get up and running quickly. Please do not use Github Issues to report personal support\n                requests.</p>\n\n              <h3>Found a bug?</h3>\n              <p>If you find a bug, please read the\n                <a target=\"_blank\" href=\"https://github.com/guonanci/react-images-viewer/blob/master/.github/CONTRIBUTING.md\">Contribution Guildelines</a> before you\n                <a target=\"_blank\" href=\"https://github.com/guonanci/react-images-viewer/issues\">report the issue</a>.</p>\n            </section>\n          </div>\n        </div>\n      </div>\n      <footer class=\"page-footer\">\n        <span class=\"page-footer__copyright--small\">Copyright </span>\n        <span class=\"page-footer__copyright--large\">&copy; </span>\n        <a href=\"https://www.jianshu.com/u/1a404f3eb7d9\" target=\"_blank\">Guo Nanci</a> 2018\n      </footer>\n    </div>\n  </div>\n  <script>\n    document.getElementById('github-stars-button').dataset.style = window.innerWidth > 480 ? 'mega' : null;\n  </script>\n  <script src=\"common.js\"></script>\n  <script src=\"app.js\"></script>\n  <script async defer id=\"github-bjs\" src=\"https://buttons.github.io/buttons.js\"></script>\n</body>\n"
  },
  {
    "path": "examples/src/index_CN.html",
    "content": "<!doctype html>\n<head>\n  <meta charset=\"UTF-8\">\n  <title>React-Images-Viewer</title>\n  <meta name=\"viewport\" content=\"width=device-width, user-scalable=no, maximum-initial-scale=1.0, initial-scale=1.0\">\n  <meta name=\"keywords\" content=\"react,reactjs,react component,imgsViewer,react images,react-images,react images viewer,react photos,react photos list,react-images-viewer,ui,javascript\">\n  <meta name=\"description\" content=\"A react library that view photos list easily, and a simple, responsive imgsViewer component for display an array of images\">\n  <meta name=\"og:locale\" content=\"zh-cn\">\n  <meta name=\"og:title\" content=\"React-Images-Viewer\">\n  <meta name=\"og:description\" content=\"A react library that view photos list easily, and a simple, responsive imgsViewer component for display an array of images\">\n  <meta name=\"og:url\" content=\"https://guonanci.github.io/react-images-viewer\">\n  <meta name=\"og:site_name\" content=\"React-Images-Viewer\">\n  <meta name=\"og:type\" content=\"article\">\n  <link rel=\"stylesheet\" href=\"example.css\">\n  <link rel=\"icon\" type=\"image/x-icon\" href=\"favicon.ico\">\n</head>\n\n<body>\n  <div class=\"page-wrapper\">\n    <div class=\"container\">\n      <nav class=\"left-col\">\n        <ul class=\"page-nav\">\n          <li class=\"page-nav__item\">\n            <a href=\"#example\" class=\"page-nav__link\">例子</a>\n          </li>\n          <li class=\"page-nav__item\">\n            <a href=\"#getting-started\" class=\"page-nav__link\">入门</a>\n          </li>\n          <li class=\"page-nav__item\">\n            <a href=\"#options\" class=\"page-nav__link\">选项</a>\n          </li>\n          <li class=\"page-nav__item\">\n            <a href=\"#license\" class=\"page-nav__link\">许可证</a>\n          </li>\n          <li class=\"page-nav__item\">\n            <a href=\"#help\" class=\"page-nav__link\">帮助</a>\n          </li>\n          <li class=\"page-nav__item\">\n            <a href=\"./index.html\" class=\"page-nav__link\">English</a>\n          </li>\n        </ul>\n      </nav>\n      <div class=\"right-col\">\n        <div class=\"page-content\">\n          <header class=\"page-header\">\n            <h1 class=\"page-header__title\">React Images Viewer</h1>\n            <p class=\"page-header__subtitle\">一个简单易用，响应式，放大并查看一组图片的 React 库。</p>\n          </header>\n          <div class=\"page-subheader\">\n            <a href=\"https://github.com/guonanci/react-images-viewer\" class=\"page-subheader__link\" target=\"_blank\">Github上的源码，文档</a>\n            <span class=\"page-subheader__button\">\n              <a href=\"https://github.com/guonanci/react-images-viewer\" id=\"github-stars-button\" class=\"github-button\" data-style=\"\" data-count-href=\"/guonanci/react-images-viewer/stargazers\"\n                data-count-api=\"/repos/\"></a>\n            </span>\n          </div>\n          <div class=\"page-body\">\n            <section id=\"examples\" class=\"section-examples\">\n              <h2>例子</h2>\n              <div id=\"example-CN\"></div>\n            </section>\n            <section id=\"getting-started\" class=\"section-getting-started\">\n              <h2>入门</h2>\n              <p>1. 依靠 yarn 来安装</p>\n              <pre>yarn add react-images</pre>\n              <p>… 或者 npm:</p>\n              <pre>npm install react-images --save</pre>\n              <p>2. JSX 使用案例:</p>\n              <pre>\n                <code>\n  &lt;Viewer\n    imgs={[\n      { src: '../images/photo-1.jpg' },\n      { src: '../images/photo-2.jpg' }\n    ]}\n    isOpen={this.state.isOpen}\n    onClickPrev={this.gotoPrevImg}\n    onClickNext={this.gotoNextImg}\n    onClose={this.closeImgsViewer}\n  /&gt;\n                </code>\n              </pre>\n            </section>\n            <section id=\"options\" class=\"section-options\">\n              <h2>选项</h2>\n              <div class=\"options-table-container\">\n                <table class=\"options-table\">\n                  <thead>\n                    <tr>\n                      <th align=\"left\">属性名</th>\n                      <th align=\"left\">类型</th>\n                      <th align=\"left\">默认值</th>\n                      <th align=\"left\">描述</th>\n                    </tr>\n                  </thead>\n                  <tbody>\n                    <tr>\n                      <td align=\"left\">backdropCloseable</td>\n                      <td align=\"left\">布尔值</td>\n                      <td align=\"left\">false</td>\n                      <td align=\"left\">是否可以通过点击幕布退出查看</td>\n                    </tr>\n                    <tr>\n                      <td align=\"left\">currImg</td>\n                      <td align=\"left\">数值型</td>\n                      <td align=\"left\">0</td>\n                      <td align=\"left\">当前图片初始化索引</td>\n                    </tr>\n                    <tr>\n                      <td align=\"left\">customControls</td>\n                      <td align=\"left\">数组</td>\n                      <td align=\"left\">undefined</td>\n                      <td align=\"left\">照片查看器顶部控件的元素数组</td>\n                    </tr>\n                    <tr>\n                      <td align=\"left\">enableKeyboardInput</td>\n                      <td align=\"left\">布尔值</td>\n                      <td align=\"left\">true</td>\n                      <td align=\"left\">支持键盘输入 -\n                        <code>空格键，Esc</code>,\n                        <code>左箭头，上箭头</code>, and\n                        <code>右箭头，下箭头</code>\n                      </td>\n                    </tr>\n                    <tr>\n                      <td align=\"left\">\n                        <a href=\"#images\">imgs</a>\n                      </td>\n                      <td align=\"left\">数组</td>\n                      <td align=\"left\">undefined</td>\n                      <td align=\"left\">必须项。一个包含具备img 元素src，srcset 合法值的对象数组。</td>\n                    </tr>\n                    <tr>\n                      <td align=\"left\">imgCountSeparator</td>\n                      <td align=\"left\">string</td>\n                      <td align=\"left\">' / '</td>\n                      <td align=\"left\">图片计数器分隔符</td>\n                    </tr>\n                    <tr>\n                      <td align=\"left\">isOpen</td>\n                      <td align=\"left\">布尔值</td>\n                      <td align=\"left\">false</td>\n                      <td align=\"left\">图片查看器是否非法</td>\n                    </tr>\n                    <tr>\n                      <td align=\"left\">onClickPrev</td>\n                      <td align=\"left\">func</td>\n                      <td align=\"left\">undefined</td>\n                      <td align=\"left\">请求上一张图片时触发</td>\n                    </tr>\n                    <tr>\n                      <td align=\"left\">onClickNext</td>\n                      <td align=\"left\">func</td>\n                      <td align=\"left\">undefined</td>\n                      <td align=\"left\">请求下一张图片时触发</td>\n                    </tr>\n                    <tr>\n                      <td align=\"left\">onClickImg</td>\n                      <td align=\"left\">func</td>\n                      <td align=\"left\">undefined</td>\n                      <td align=\"left\">当前图片的点击事件处理器</td>\n                    </tr>\n                    <tr>\n                      <td align=\"left\">onClickThumbnail</td>\n                      <td align=\"left\">func</td>\n                      <td align=\"left\">undefined</td>\n                      <td align=\"left\">缩略图的点击事件处理器</td>\n                    </tr>\n                    <tr>\n                      <td align=\"left\">onClose</td>\n                      <td align=\"left\">func</td>\n                      <td align=\"left\">undefined</td>\n                      <td align=\"left\">必须项，照片查看器的关闭事件处理器</td>\n                    </tr>\n                    <tr>\n                      <td align=\"left\">preloadNextImg</td>\n                      <td align=\"left\">布尔值</td>\n                      <td align=\"left\">true</td>\n                      <td align=\"left\">是否预加载下一张图片</td>\n                    </tr>\n                    <tr>\n                      <td align=\"left\">preventScroll</td>\n                      <td align=\"left\">布尔值</td>\n                      <td align=\"left\">true</td>\n                      <td align=\"left\">自动滚动是否阻止</td>\n                    </tr>\n                    <tr>\n                      <td align=\"left\">showCloseBtn</td>\n                      <td align=\"left\">布尔值</td>\n                      <td align=\"left\">true</td>\n                      <td align=\"left\">是否在右上角显示 X 关闭按钮</td>\n                    </tr>\n                    <tr>\n                      <td align=\"left\">showImgCount</td>\n                      <td align=\"left\">布尔值</td>\n                      <td align=\"left\">true</td>\n                      <td align=\"left\">是否显示图片索引，比如“3 / 20”</td>\n                    </tr>\n                    <tr>\n                      <td align=\"left\">showThumbnails</td>\n                      <td align=\"left\">布尔值</td>\n                      <td align=\"left\">false</td>\n                      <td align=\"left\">是否在查看器下方显示缩略图</td>\n                    </tr>\n                    <tr>\n                      <td align=\"left\">theme</td>\n                      <td align=\"left\">对象</td>\n                      <td align=\"left\">undefined</td>\n                      <td align=\"left\">传递给每个组件的主题化样式；\n                        <code>arrow键</code>,\n                        <code>container键</code>，等等。</td>\n                    </tr>\n                    <tr>\n                      <td align=\"left\">width</td>\n                      <td align=\"left\">数值型</td>\n                      <td align=\"left\">1024</td>\n                      <td align=\"left\">轮播器的最大宽度，默认为1024px</td>\n                    </tr>\n                  </tbody>\n                </table>\n              </div>\n              <h2>Imgs</h2>\n              <div class=\"options-table-container\" id=\"images\">\n                <table class=\"options-table\">\n                  <thead>\n                    <tr>\n                      <th align=\"left\">属性名</th>\n                      <th align=\"left\">类型</th>\n                      <th align=\"left\">默认值</th>\n                      <th align=\"left\">描述</th>\n                    </tr>\n                  </thead>\n                  <tbody>\n                    <tr>\n                      <td align=\"left\">src</td>\n                      <td align=\"left\">字符串</td>\n                      <td align=\"left\">undefined</td>\n                      <td align=\"left\">必须项. 主图片路径</td>\n                    </tr>\n                    <tr>\n                      <td align=\"left\">srcset</td>\n                      <td align=\"left\">arr</td>\n                      <td align=\"left\">undefined</td>\n                      <td align=\"left\">可替代图片尺寸列表</td>\n                    </tr>\n                    <tr>\n                      <td align=\"left\">caption</td>\n                      <td align=\"left\">字符串</td>\n                      <td align=\"left\">undefined</td>\n                      <td align=\"left\">当前图片下显示，对于描述或者归属很有用。</td>\n                    </tr>\n                    <tr>\n                      <td align=\"left\">thumbnail</td>\n                      <td align=\"left\">字符串</td>\n                      <td align=\"left\">undefined</td>\n                      <td align=\"left\">\n                        <code>showThumbnails</code>被设置成\n                        <code>true</code>的话要显示的缩略图\n                      </td>\n                    </tr>\n                  </tbody>\n                </table>\n              </div>\n            </section>\n\n            <section id=\"license\" class=\"section-license\">\n              <h2>许可证</h2>\n              <p>React Images Viewer在\n                <a target=\"_blank\" href=\"https://github.com/guonanci/react-images-viewer/blob/master/LICENSE\">MIT证书</a>下，对于个人或者商业用途都是免费的</p>。\n              <p>它不是必须项，但是十分感谢。没有面向用户而且保留源代码。</p>\n            </section>\n\n            <section id=\"help\" class=\"section-help\">\n              <h2>帮助</h2>\n\n              <h3>需要帮助？</h3>\n              <p>遵循\n                <a target=\"_blank\" href=\"https://github.com/guonanci/react-images-viewer#quick-start\">github 上的快速入门指南</a>，并运行起来。 请不要出于个人目的滥用 github issues。\n              </p>\n\n              <h3>发现 bug？</h3>\n              <p>如果你找到 bug 了，请在你\n                <a target=\"_blank\" href=\"https://github.com/guonanci/react-images-viewer/issues\">报告issue</a>.</p>\n                之前阅读<a target=\"_blank\" href=\"https://github.com/guonanci/react-images-viewer/blob/master/.github/CONTRIBUTING.md\">贡献指南</a>一番。\n            </section>\n          </div>\n        </div>\n      </div>\n      <footer class=\"page-footer\">\n        <span class=\"page-footer__copyright--small\">贡献指南</span>\n        <span class=\"page-footer__copyright--large\">&copy; </span>\n        <a href=\"https://www.jianshu.com/u/1a404f3eb7d9\" target=\"_blank\">郭南赐</a> 2018\n      </footer>\n    </div>\n  </div>\n  <script>\n    document.getElementById('github-stars-button').dataset.style = window.innerWidth > 480 ? 'mega' : null;\n  </script>\n  <script src=\"common.js\"></script>\n  <script src=\"app_CN.js\"></script>\n  <script async defer id=\"github-bjs\" src=\"https://buttons.github.io/buttons.js\"></script>\n</body>\n"
  },
  {
    "path": "examples/src/standalone.html",
    "content": "<!doctype html>\n<head>\n  <meta charset=\"UTF-8\">\n  <title>React-Images-Viewer</title>\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scable=no, maximum-scale=1, width=device-width\">\n  <meta name=\"keywords\" content=\"react,reactjs,react component,imgsViewer,react images,react-images,react images viewer,react photos,react photos list,react-images-viewer,ui,javascript\">\n  <meta name=\"description\" content=\"A react library that view photos list easily, and a simple, responsive imgsViewer component for display an array of images\">\n  <meta name=\"og:locale\" content=\"zh-cn\">\n  <meta property=\"og:title\" content=\"React-Images\">\n  <meta property=\"og:description\" content=\"A simple, responsive Lightbox component for displaying an array of images.\">\n  <meta property=\"og:url\" content=\"https://guonanci.github.io/react-images-viewer\">\n  <meta property=\"og:site_name\" content=\"React-Images-Viewer\">\n  <meta property=\"og:type\" content=\"article\">\n  <link rel=\"stylesheet\" href=\"example.css\">\n  <link rel=\"icon\" type=\"image/x-icon\" href=\"favicon.ico\">\n  <meta http-equiv=\"X-UA-Compatible\" content=\"ie=edge\">\n</head>\n<body>\n  <div class=\"page-wrapper\">\n    <div id=\"app\">\n\n    </div>\n  </div>\n  <script src=\"https://unpkg.com/babel-standalone@6/babel.min.js\" charset=\"utf-8\"></script>\n  <script src=\"https://unpkg.com/react@15.6.1/dist/react.js\"></script>\n  <script src=\"https://unpkg.com/react-dom@15.6.1/dist/react-dom.js\"></script>\n  <script src=\"https://unpkg.com/prop-types@15.5.10/prop-types.js\"></script>\n  <script src=\"https://unpkg.com/aphrodite/dist/aphrodite.umd.min.js\"></script>\n  <script src=\"https://cdnjs.cloudflare.com/ajax/libs/react-transition-group/1.1.3/react-transition-group.js\"></script>\n  <script src=\"https://unpkg.com/react-photo-gallery@6.0.13/dist/react-photo-gallery.min.js\"></script>\n  <script src=\"https://rawgit.com/neptunian/react-scrolllock/master/dist/react-scrolllock.min.js\"></script>\n  <script src=\"https://unpkg.com/react-images-viewer@1.0.0/dist/react-images-viewer.min.js\"></script>\n  <script type=\"text/babel\">\n    const photos = [\n      { src: 'https://images.unsplash.com/photo-1526784725085-c69e947bf92e?w=1024&h=1024', width:1, height:1 },\n      { src: 'https://images.unsplash.com/photo-1522985225914-17a10a58c8ec?w=1024&h=1024', width:1, height:1 },\n      { src: 'https://images.unsplash.com/photo-1522931698295-e7b4d3e4188f?w=1024&h=1024', width:1, height:1 },\n      { src: 'https://images.unsplash.com/photo-1516175663209-ac2459a5652f?w=1024&h=1024', width:1, height:1 },\n      { src: 'https://images.unsplash.com/photo-1515911601378-97de98db6dda?w=1024&h=1024', width:1, height:1 },\n    ]\n    class App extends React.Component {\n      constructor () {\n        super()\n        this.state = {\n          currImg: 0\n        }\n        this.closeImgsViewer = this.closeImgsViewer.bind(this)\n        this.openViewer = this.openViewer.bind(this)\n        this.gotoNext = this.gotoNext.bind(this)\n        this.gotoPrev = this.gotoPrev.bind(this)\n      }\n      openViewer (event, obj) {\n        this.setState({\n          currImg: obj.index,\n          viewerIsOpen: true,\n        })\n      }\n      closeImgsViewer () {\n        this.setState({\n          currImg: 0,\n          viewerIsOpen: false,\n        })\n      }\n      gotoPrev () {\n        this.setState({\n          currImg: this.state.currImg - 1\n        })\n      }\n      gotoNext () {\n        this.setState({\n          currImg: this.state.currImg + 1\n        })\n      }\n      render () {\n        return (\n          <div>\n            <Gallery photos={photos} onClick={this.openViewer} />\n            <ImgsViewer\n              imgs={photos}\n              onClose={this.closeImgsViewer}\n              onClickPrev={this.gotoPrev}\n              onClickNext={this.gotoNext}\n              currImg={this.state.currImg}\n              isOpen={this.state.viewerIsOpen}\n            />\n          </div>\n        )\n      }\n    }\n  ReactDOM.render(<App />, document.getElementById('app'))\n  </script>\n</body>\n"
  },
  {
    "path": "index.js",
    "content": "'use strict';\n\nmodule.exports = function(number, locale) {\n  return number.toLocaleString(locale);\n};\n"
  },
  {
    "path": "jest.config.js",
    "content": "module.exports = {\n  verbose: true,\n  collectCoverage: true,\n  collectCoverageFrom: ['src/*.{js, jsx}'],\n  setupFiles: ['./__test__/rafShim.js', './__test__/jestsetup.js'],\n  testURL: 'http://localhost/',\n  // moduleFileExtensions: [\n  //   'js',\n  //   'jsx',\n  // ],\n  // moduleNameMapper: {\n  //   '\\\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': '<rootDir>/__mocks__/fileMock.js',\n  //   '\\\\.(css|less)$': '<rootDir>/__mocks__/styleMock.js',\n  // },\n  // transform: {\n  //   '^.+\\\\.js$': 'babel-jest',\n  // }\n}\n"
  },
  {
    "path": "new-branch.sh",
    "content": "git checkout -b $1\ngit push -u origin $1\ngit branch --all\n"
  },
  {
    "path": "package-scripts.js",
    "content": "const npsUtils = require('nps-utils')\nconst path = require('path')\nconst { series, rimraf, concurrent }  = npsUtils\n\nmodule.exports = {\n  scripts: {\n    build: {\n      description: 'clean dist directory and run all builds',\n      default: series(\n        rimraf('dist'),\n        rimraf('lib'),\n        concurrent.nps('build.rollup', 'build.babel', 'build.less')\n      ),\n      rollup: 'rollup --config',\n      babel: 'babel src -d lib',\n      less: series(\n        'lessc examples/src/example.less examples/dist/example.css'\n      ),\n    },\n    publish: {\n      default: series(\n        rimraf('examples/dist'),\n        'webpack --progress -p',\n        // 'git pull',\n        'git subtree push --prefix examples/dist origin gh-pages'\n        // 'git push origin `git subtree split --prefix examples/dist gh-pages`:gh-pages --force'\n      )\n    }\n  }\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"react-images-viewer\",\n  \"version\": \"1.7.1\",\n  \"description\": \"Create an react-images-viewer component.\",\n  \"main\": \"lib/ImgsViewer.js\",\n  \"jsnext:main\": \"dist/react-images-viewer.es.js\",\n  \"scripts\": {\n    \"build\": \"nps build\",\n    \"publish:examples\": \"NODE_ENV=production nps publish\",\n    \"start\": \"webpack-dev-server --progress\",\n    \"lint\": \"eslint src/** examples/src/app.js examples/src/components/Gallery.js\",\n    \"test\": \"jest\",\n    \"coverage\": \"jest --coverage && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/guonanci/react-images-viewer.git\"\n  },\n  \"keywords\": [\n    \"react\",\n    \"react-component\",\n    \"react images\",\n    \"react-images\",\n    \"react-images-viewer\",\n    \"lightbox\",\n    \"react lightbox\",\n    \"react-lightbox\",\n    \"carousel\",\n    \"react-carousel\",\n    \"react carousel\",\n    \"gallery\",\n    \"react-gallery\",\n    \"react gallery\",\n    \"react-photo\",\n    \"react-photo-viewer\"\n  ],\n  \"author\": \"yiminanci <yiminanci@gmail.com> (https://juejin.cn/user/3491704660305111)\",\n  \"license\": \"MIT\",\n  \"responsitory\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/guonanci/react-images-viewer.git\"\n  },\n  \"bugs\": {\n    \"url\": \"https://github.com/guonanci/react-images-viewer/issues\"\n  },\n  \"homepage\": \"https://github.com/guonanci/react-images-viewer#readme\",\n  \"devDependencies\": {\n    \"babel\": \"^6.23.0\",\n    \"babel-cli\": \"^6.26.0\",\n    \"babel-core\": \"^6.26.3\",\n    \"babel-eslint\": \"^8.2.5\",\n    \"babel-jest\": \"^23.4.2\",\n    \"babel-loader\": \"^7.1.5\",\n    \"babel-plugin-external-helpers\": \"^6.22.0\",\n    \"babel-plugin-transform-object-assign\": \"^6.22.0\",\n    \"babel-plugin-transform-object-rest-spread\": \"^6.26.0\",\n    \"babel-plugin-transform-react-remove-prop-types\": \"^0.4.13\",\n    \"babel-preset-env\": \"^1.7.0\",\n    \"babel-preset-react\": \"^6.24.1\",\n    \"coveralls\": \"^3.0.2\",\n    \"css-loader\": \"^1.0.0\",\n    \"enzyme\": \"^3.3.0\",\n    \"enzyme-adapter-react-16\": \"^1.15.6\",\n    \"eslint\": \"^5.1.0\",\n    \"eslint-plugin-react\": \"^7.10.0\",\n    \"html-loader\": \"^0.5.5\",\n    \"html-webpack-plugin\": \"^3.2.0\",\n    \"jest\": \"^23.4.2\",\n    \"jest-cli\": \"^23.4.2\",\n    \"less\": \"^3.5.3\",\n    \"less-loader\": \"^4.1.0\",\n    \"mini-css-extract-plugin\": \"^0.4.1\",\n    \"nps\": \"^5.9.2\",\n    \"nps-utils\": \"^1.6.0\",\n    \"react\": \"^17.0.2\",\n    \"react-dom\": \"^17.0.2\",\n    \"react-test-renderer\": \"^16.4.1\",\n    \"regenerator-runtime\": \"^0.12.0\",\n    \"rollup\": \"^0.62.0\",\n    \"rollup-plugin-babel\": \"^3.0.7\",\n    \"rollup-plugin-commonjs\": \"^9.1.3\",\n    \"rollup-plugin-node-resolve\": \"^3.3.0\",\n    \"rollup-plugin-uglify\": \"^4.0.0\",\n    \"style-loader\": \"^0.21.0\",\n    \"uglify-es\": \"^3.3.9\",\n    \"webpack\": \"^4.16.2\",\n    \"webpack-cli\": \"^3.1.0\",\n    \"webpack-dev-server\": \"^3.1.5\"\n  },\n  \"peerDependencies\": {\n    \"react\": \"^15.0 || ^16.0 || ^17.0\",\n    \"react-dom\": \"^15.0 || ^16.0 || ^17.0\"\n  },\n  \"dependencies\": {\n    \"aphrodite\": \"^2.2.2\",\n    \"prop-types\": \"^15.6.2\",\n    \"react-scrolllock\": \"^3.0.1\",\n    \"react-transition-group\": \"^2.4.0\"\n  }\n}\n"
  },
  {
    "path": "rollup.config.js",
    "content": "import babel from 'rollup-plugin-babel'\nimport resolve from 'rollup-plugin-node-resolve'\nimport { uglify } from 'rollup-plugin-uglify'\nimport { minify } from 'uglify-es'\n\nconst name = 'ImgsViewer'\nconst path = 'dist/react-images-viewer'\nconst globals = {\n  'prop-types': 'PropTypes',\n  'react-dom': 'ReactDOM',\n  react: 'React',\n  aphrodite: 'aphrodite',\n  'aphrodite/no-important': 'aphrodite',\n  'react-scrolllock': 'ScrollLock',\n  'react-transition-group': 'ReactTransitionGroup',\n  'react-spinners': 'BounceLoader',\n}\n\nconst external = Object.keys(globals)\nconst babelOptions = (production) => {\n  let result = {\n    babelrc: false,\n    presets: ['react', ['env', { modules: false }]],\n    plugins: [['transform-object-rest-spread', { useBuiltIns: true }], 'transform-object-rest-spread', 'external-helpers']\n  }\n  if (production) {\n    result.plugins.push('transform-react-remove-prop-types')\n  }\n  return result\n}\n\nexport default [\n  {\n    input: 'src/ImgsViewer.js',\n    output: {\n      file: path + '.es.js',\n      format: 'es',\n    },\n    external: external,\n    plugins: [babel(babelOptions(false))]\n  },\n  {\n    input: 'src/ImgsViewer.js',\n    output: {\n      name: name,\n      file: path + '.js',\n      format: 'umd',\n      globals: globals,\n    },\n    external: external,\n    plugins: [babel(babelOptions(false)), resolve()]\n  },\n  {\n    input: 'src/ImgsViewer.js',\n    output: {\n      name: name,\n      file: path + '.min.js',\n      format: 'umd',\n      globals: globals,\n    },\n    external: external,\n    plugins: [babel(babelOptions(true)), resolve(), uglify({}, minify)],\n  }\n]\n"
  },
  {
    "path": "src/ImgsViewer.js",
    "content": "import PropTypes from \"prop-types\";\nimport React, { Component, Fragment } from \"react\";\nimport { StyleSheet, css } from \"aphrodite\";\nimport ScrollLock from \"react-scrolllock\";\n\nimport defaultTheme from \"./theme\";\nimport Arrow from \"./components/Arrow\";\nimport Container from \"./components/Container\";\nimport Footer from \"./components/Footer\";\nimport Header from \"./components/Header\";\nimport PaginatedThumbnails from \"./components/PaginatedThumbnails\";\nimport Portal from \"./components/Portal\";\nimport DefaultSpinner from \"./components/Spinner\";\n\nimport { bindFunctions, canUseDom, deepMerge } from \"./utils/util\";\n\nfunction normalizeSourceSet(data) {\n  const sourceSet = data.srcSet || data.srcset;\n\n  if (Array.isArray(sourceSet)) {\n    return sourceSet.join();\n  }\n\n  return sourceSet;\n}\n\nconst ThemeContext = React.createContext({\n  theme: defaultTheme,\n  toggleTheme: (newTheme) => {},\n});\n\nclass ImgsViewer extends Component {\n  constructor(props) {\n    super(props);\n\n    this.theme = deepMerge(defaultTheme, this.props.theme);\n    this.classes = StyleSheet.create(\n      deepMerge(defaultStyles, this.props.theme)\n    );\n    this.toggleTheme = (theme) => {\n      this.setState(() => ({ theme }));\n    };\n    this.state = {\n      imgLoaded: false,\n      theme: this.theme,\n      toggleTheme: this.toggleTheme,\n    };\n\n    bindFunctions.call(this, [\n      \"gotoNext\",\n      \"gotoPrev\",\n      \"closeBackdrop\",\n      \"handleKeyboardInput\",\n      \"handleImgLoaded\",\n    ]);\n  }\n  componentDidMount() {\n    if (this.props.isOpen) {\n      if (this.props.enableKeyboardInput) {\n        window.addEventListener(\"keydown\", this.handleKeyboardInput);\n      }\n      if (typeof this.props.currImg === \"number\") {\n        this.preloadImg(this.props.currImg, this.handleImgLoaded);\n      }\n    }\n  }\n  // static getDerivedStateFromProps (nextProps, prevState) {\n  UNSAFE_componentWillReceiveProps(nextProps) {\n    if (!canUseDom) return;\n\n    // const instance = this\n\n    // always to preload imgs with both directions\n    // then when user changs direction, img also show quickly\n    if (nextProps.preloadNextImg) {\n      const nextIdx = nextProps.currImg + 1;\n      const prevIdx = nextProps.currImg - 1;\n      // debugger\n      // if (!this) return null\n      this.preloadImg(prevIdx);\n      this.preloadImg(nextIdx);\n    }\n    // preload currImg\n    if (\n      this.props.currImg !== nextProps.currImg ||\n      (!this.props.isOpen && nextProps.isOpen)\n    ) {\n      const img = this.preloadImgData(\n        nextProps.imgs[nextProps.currImg],\n        this.handleImgLoaded\n      );\n      if (img)\n        this.setState({\n          imgLoaded: img.complete,\n        });\n    }\n\n    // add/remove event listeners\n    if (\n      !this.props.isOpen &&\n      nextProps.isOpen &&\n      nextProps.enableKeyboardInput\n    ) {\n      window.addEventListener(\"keydown\", this.handleKeyboardInput);\n    }\n    if (!nextProps.isOpen && nextProps.enableKeyboardInput) {\n      window.removeEventListener(\"keydown\", this.handleKeyboardInput);\n    }\n\n    return null;\n  }\n  componentWillUnmount() {\n    if (this.props.enableKeyboardInput) {\n      window.removeEventListener(\"keydown\", this.handleKeyboardInput);\n    }\n  }\n\n  // ====================\n  // Methods\n  // ====================\n\n  preloadImg(idx, onload) {\n    return this.preloadImgData(this.props.imgs[idx], onload);\n  }\n  preloadImgData(data, onload) {\n    if (!data) return;\n\n    const img = new Image();\n    const sourceSet = normalizeSourceSet(data);\n\n    // Todo: add error handling for missing imgs\n    img.onerror = onload;\n    img.onload = onload;\n    img.src = data.src;\n\n    if (sourceSet) img.srcset = sourceSet;\n\n    return img;\n  }\n  gotoNext(event) {\n    const { currImg, imgs } = this.props;\n    const { imgLoaded } = this.state;\n\n    if (!imgLoaded || currImg === imgs.length - 1) return;\n\n    if (event) {\n      event.preventDefault();\n      event.stopPropagation();\n    }\n\n    this.props.onClickNext();\n  }\n  gotoPrev(event) {\n    const { currImg } = this.props;\n    const { imgLoaded } = this.state;\n\n    if (!imgLoaded || currImg === 0) return;\n\n    if (event) {\n      event.preventDefault();\n      event.stopPropagation();\n    }\n\n    this.props.onClickPrev();\n  }\n  closeBackdrop(event) {\n    if (\n      event.target.id === \"viewerBackdrop\" ||\n      event.target.tagName === \"FIGURE\"\n    ) {\n      this.props.onClose();\n    }\n  }\n  handleKeyboardInput(event) {\n    const { keyCode } = event;\n    if (keyCode === 37 || keyCode === 33 || keyCode === 38) {\n      // left, pageup, up\n      this.gotoPrev(event);\n      return true;\n    } else if (keyCode === 39 || keyCode === 34 || keyCode === 40) {\n      // right, pagedown, down\n      this.gotoNext(event);\n      return true;\n    } else if (keyCode === 27 || keyCode === 32) {\n      // esc, space\n      this.props.onClose();\n      return true;\n    }\n    return false;\n  }\n  handleImgLoaded() {\n    this.setState({\n      imgLoaded: true,\n    });\n  }\n\n  // ====================\n  // Renderers\n  // ====================\n\n  renderArrowPrev(theme) {\n    if (this.props.currImg === 0) return null;\n\n    return (\n      <Arrow\n        theme={theme}\n        direction=\"left\"\n        icon=\"arrowLeft\"\n        onClick={this.gotoPrev}\n        title={this.props.leftArrowTitle}\n        type=\"button\"\n      />\n    );\n  }\n  renderArrowNext(theme) {\n    if (this.props.currImg === this.props.imgs.length - 1) return null;\n\n    return (\n      <Arrow\n        theme={theme}\n        direction=\"right\"\n        icon=\"arrowRight\"\n        onClick={this.gotoNext}\n        title={this.props.rightArrowTitle}\n        type=\"button\"\n      />\n    );\n  }\n  renderDialog(newState) {\n    const { backdropCloseable, isOpen, showThumbnails, width } = this.props;\n\n    const { imgLoaded } = this.state;\n\n    if (!isOpen) return <span key=\"closed\" />;\n\n    const offsetThumbnails = showThumbnails\n      ? this.theme.thumbnail.size + this.theme.container.gutter.vertical\n      : 0;\n\n    return (\n      <ThemeContext.Consumer>\n        {({ theme, toggleTheme }) => {\n          theme = newState.theme;\n          return (\n            <Container\n              theme={theme}\n              key=\"open\"\n              onClick={backdropCloseable && this.closeBackdrop}\n              onTouchEnd={backdropCloseable && this.closeBackdrop}\n            >\n              <Fragment>\n                <div\n                  className={css(this.classes.content)}\n                  style={{\n                    marginBottom: offsetThumbnails,\n                    maxWidth: width,\n                  }}\n                >\n                  {imgLoaded && this.renderHeader(theme)}{\" \"}\n                  {this.renderImgs(theme)}\n                  {this.renderSpinner()} {imgLoaded && this.renderFooter(theme)}\n                </div>\n                {imgLoaded && this.renderThumbnails(theme)}\n                {imgLoaded && this.renderArrowPrev(theme)}\n                {imgLoaded && this.renderArrowNext(theme)}\n                {this.props.preventScroll && <ScrollLock />}\n              </Fragment>\n            </Container>\n          );\n        }}\n      </ThemeContext.Consumer>\n    );\n  }\n  renderImgs(theme) {\n    const { currImg, imgs, onClickImg, showThumbnails } = this.props;\n\n    const { imgLoaded } = this.state;\n\n    if (!imgs || !imgs.length) return null;\n\n    const img = imgs[currImg];\n    const sourceSet = normalizeSourceSet(img);\n    const sizes = sourceSet ? \"100vw\" : null;\n\n    const thumbnailsSize = showThumbnails ? theme.thumbnail.size : 0;\n    const heightOffset = `${\n      theme.header.height +\n      theme.footer.height +\n      thumbnailsSize +\n      theme.container.gutter.vertical\n    }px`;\n\n    return (\n      <figure className={css(this.classes.figure)}>\n        <img\n          className={css(this.classes.img, imgLoaded && this.classes.imgLoaded)}\n          onClick={onClickImg}\n          sizes={sizes}\n          alt={img.alt}\n          src={img.src}\n          srcSet={sourceSet}\n          style={{\n            cursor: onClickImg ? \"pointer\" : \"auto\",\n            maxHeight: `calc(100vh - ${heightOffset}`,\n          }}\n        />\n      </figure>\n    );\n  }\n  renderThumbnails(theme) {\n    const {\n      imgs,\n      currImg,\n      leftArrowTitle,\n      rightArrowTitle,\n      onClickThumbnail,\n      showThumbnails,\n      thumbnailOffset,\n    } = this.props;\n\n    if (!showThumbnails) return null;\n\n    return (\n      <PaginatedThumbnails\n        theme={theme}\n        leftTitle={leftArrowTitle}\n        rightTitle={rightArrowTitle}\n        currImg={currImg}\n        imgs={imgs}\n        offset={thumbnailOffset}\n        onClickThumbnail={onClickThumbnail}\n      />\n    );\n  }\n  renderHeader(theme) {\n    const { closeBtnTitle, customControls, onClose, showCloseBtn } = this.props;\n\n    return (\n      <Header\n        theme={theme}\n        customControls={customControls}\n        onClose={onClose}\n        showCloseBtn={showCloseBtn}\n        closeBtnTitle={closeBtnTitle}\n      />\n    );\n  }\n  renderFooter(theme) {\n    const { currImg, imgs, imgCountSeparator, showImgCount } = this.props;\n\n    if (!imgs || !imgs.length) return null;\n\n    return (\n      <Footer\n        theme={theme}\n        caption={imgs[currImg].caption}\n        countCurr={currImg + 1}\n        countSeparator={imgCountSeparator}\n        countTotal={imgs.length}\n        showCount={showImgCount}\n      />\n    );\n  }\n  renderSpinner() {\n    const { spinner, spinnerDisabled, spinnerColor, spinnerSize } = this.props;\n\n    const { imgLoaded } = this.state;\n    const Spinner = spinner;\n    if (spinnerDisabled) return null;\n    return (\n      <div\n        className={css(\n          this.classes.spinner,\n          !imgLoaded && this.classes.spinnerActive\n        )}\n      >\n        <Spinner color={spinnerColor} size={spinnerSize} />\n      </div>\n    );\n  }\n\n  render() {\n    return (\n      <ThemeContext.Provider value={this.state}>\n        <Portal> {this.renderDialog(this.state)} </Portal>\n      </ThemeContext.Provider>\n    );\n  }\n}\n\nImgsViewer.propTypes = {\n  backdropCloseable: PropTypes.bool,\n  closeBtnTitle: PropTypes.string,\n  currImg: PropTypes.number,\n  customControls: PropTypes.arrayOf(PropTypes.node),\n  enableKeyboardInput: PropTypes.bool,\n  imgCountSeparator: PropTypes.string,\n  imgs: PropTypes.arrayOf(\n    PropTypes.shape({\n      src: PropTypes.string.isRequired,\n      srcSet: PropTypes.oneOfType([PropTypes.array, PropTypes.string]),\n      caption: PropTypes.oneOfType([PropTypes.string, PropTypes.element]),\n      thumbnail: PropTypes.string,\n    })\n  ).isRequired,\n  isOpen: PropTypes.bool,\n  leftArrowTitle: PropTypes.string,\n  onClickImg: PropTypes.func,\n  onClickNext: PropTypes.func,\n  onClickPrev: PropTypes.func,\n  onClickThumbnail: PropTypes.func,\n  onClose: PropTypes.func.isRequired,\n  preloadNextImg: PropTypes.bool,\n  preventScroll: PropTypes.bool,\n  rightArrowTitle: PropTypes.string,\n  showCloseBtn: PropTypes.bool,\n  showImgCount: PropTypes.bool,\n  showThumbnails: PropTypes.bool,\n  spinnerDisabled: PropTypes.bool,\n  spinner: PropTypes.func,\n  spinnerColor: PropTypes.string,\n  spinnerSize: PropTypes.number,\n  theme: PropTypes.object,\n  thumbnailOffset: PropTypes.number,\n  width: PropTypes.number,\n};\nImgsViewer.defaultProps = {\n  currImg: 0,\n  enableKeyboardInput: true,\n  imgCountSeparator: \" / \",\n  onClickShowNextImg: true,\n  preloadNextImg: true,\n  preventScroll: true,\n  showCloseBtn: true,\n  showImgCount: true,\n  spinnerDisabled: false,\n  spinner: DefaultSpinner,\n  spinnerColor: \"#fff\",\n  spinnerSize: 50,\n  theme: {},\n  thumbnailOffset: 2,\n  width: 1024,\n};\n\nconst defaultStyles = {\n  content: {\n    position: \"relative\",\n  },\n  figure: {\n    margin: 0, // remove browser default\n  },\n  img: {\n    display: \"block\", // removes browser default gutter\n    height: \"auto\",\n    margin: \"0 auto\", // main center on very short screens or very narrow img\n    maxWidth: \"100%\",\n\n    // disable user select\n    WebkitTouchCallout: \"none\",\n    userSelect: \"none\",\n\n    // opacity animation on image load\n    opacity: 0,\n    transition: \"opacity .3s\",\n  },\n  imgLoaded: {\n    opacity: 1,\n  },\n  spinner: {\n    position: \"absolute\",\n    top: \"50%\",\n    left: \"50%\",\n    transform: \"translate(-50%, -50%)\",\n\n    // opacity animation to make spinner appear with delay\n    opacity: 0,\n    transition: \"opacity .3s\",\n    pointerEvents: \"none\",\n  },\n  spinnerActive: {\n    opacity: 1,\n  },\n};\n\nexport default ImgsViewer;\n"
  },
  {
    "path": "src/components/Arrow.js",
    "content": "import PropTypes from \"prop-types\";\nimport React from \"react\";\nimport { css, StyleSheet } from \"aphrodite/no-important\";\n\nimport defaults from \"../theme\";\nimport { deepMerge } from \"../utils/util\";\nimport Icon from \"./Icon\";\n\nfunction Arrow({ direction, icon, onClick, size, theme, ...props }) {\n  const classes = StyleSheet.create(deepMerge(defaultStyles, theme));\n\n  return (\n    <button\n      type=\"button\" // default: submit\n      className={css(\n        classes.arrow,\n        classes[\"arrow__direction__\" + direction],\n        size && classes[\"arrow__size__\" + size]\n      )}\n      onClick={onClick}\n      onTouchEnd={onClick}\n      {...props}\n    >\n      <Icon\n        fill={(!!theme.arrow && theme.arrow.fill) || defaults.arrow.fill}\n        type={icon}\n      />\n    </button>\n  );\n}\n\nArrow.propTypes = {\n  theme: PropTypes.object,\n  direction: PropTypes.oneOf([\"left\", \"right\"]),\n  icon: PropTypes.string,\n  onClick: PropTypes.func.isRequired,\n  size: PropTypes.oneOf([\"medium\", \"small\"]).isRequired,\n};\nArrow.defaultProps = {\n  size: \"medium\",\n};\nconst defaultStyles = {\n  arrow: {\n    background: \"none\",\n    border: \"none\",\n    borderRadius: 4,\n    cursor: \"pointer\",\n    outline: \"none\",\n    padding: 10, // increase hit area\n    position: \"absolute\",\n    top: \"50%\",\n\n    // disable user select\n    WebkitTouchCallout: \"none\",\n    userSelect: \"none\",\n  },\n\n  // sizes\n  arrow__size__medium: {\n    height: defaults.arrow.height,\n    marginTop: defaults.arrow.height / -2,\n    width: 40,\n\n    \"@media (min-width: 768px)\": {\n      width: 70,\n    },\n  },\n  arrow__size__small: {\n    height: defaults.thumbnail.size,\n    marginTop: defaults.thumbnail.size / -2,\n    width: 30,\n\n    \"@media (min-width: 500px)\": {\n      width: 40,\n    },\n  },\n\n  // direciton\n  arrow__direction__right: {\n    right: defaults.container.gutter.horizontal,\n  },\n  arrow__direction__left: {\n    left: defaults.container.gutter.horizontal,\n  },\n};\n\nexport default Arrow;\n"
  },
  {
    "path": "src/components/Container.js",
    "content": "import PropTypes from 'prop-types'\nimport React from 'react'\nimport { css, StyleSheet } from 'aphrodite/no-important'\n\nimport defaults from '../theme'\nimport { deepMerge } from '../utils/util'\n\nfunction Container (props) {\n  const classes = StyleSheet.create(deepMerge(defaultStyles, props.theme))\n\n  return (\n    <div\n      id=\"viewerBackdrop\"\n      className={css(classes.container)}\n      {...props}\n    />\n  )\n}\n\nContainer.propTypes = {\n  theme: PropTypes.object\n}\n\nconst defaultStyles = {\n  container: {\n    alignItems: 'center',\n    backdropColor: defaults.container.background,\n    boxSizing: 'border-box',\n    display: 'flex',\n    height: '100%',\n    justifyContent: 'center',\n    left: 0,\n    paddingTop: defaults.container.gutter.vertical,\n    paddingRight: defaults.container.gutter.horizontal,\n    paddingBottom: defaults.container.gutter.vertical,\n    paddingLeft: defaults.container.gutter.horizontal,\n    position: 'fixed',\n    top: 0,\n    width: '100%',\n    zIndex: defaults.container.zIndex,\n  }\n}\n\nexport default Container\n"
  },
  {
    "path": "src/components/Footer.js",
    "content": "import PropTypes from 'prop-types'\nimport React from 'react'\nimport { css, StyleSheet } from 'aphrodite/no-important'\nimport defaults from '../theme'\nimport { deepMerge } from '../utils/util'\n\nfunction Footer ({ caption, countCurr, countSeparator, countTotal, showCount, theme, ...props }) {\n  if (!caption && !showCount) return null\n\n  const classes = StyleSheet.create(deepMerge(defaultStyles, theme))\n\n  const imgCount = showCount ? (\n    <div className={css(classes.footerCount)}>\n      {countCurr}\n      {countSeparator}\n      {countTotal}\n    </div>\n  ) : <span />\n\n  return (\n    <div className={css(classes.footer)} {...props}>\n      {caption ? (\n        <figcaption className={css(classes.footerCaption)}>\n          {caption}\n        </figcaption>\n      ) : <span />}\n      {imgCount}\n    </div>\n  )\n}\n\nFooter.propTypes ={\n  theme: PropTypes.object,\n  caption: PropTypes.oneOfType([PropTypes.string, PropTypes.element]),\n  countCurr: PropTypes.number,\n  countSeparator: PropTypes.string,\n  countTotal: PropTypes.number,\n  showCount: PropTypes.bool,\n}\n\nconst defaultStyles = {\n  footer: {\n    boxSizing: 'border-box',\n    color: defaults.footer.color,\n    cursor: 'auto',\n    display: 'flex',\n    justifyContent: 'space-between',\n    left: 0,\n    lineHeight: 1.3,\n    paddingTop: defaults.footer.gutter.vertical,\n    paddingRight: defaults.footer.gutter.horizontal,\n    paddingBottom: defaults.footer.gutter.vertical,\n    paddingLeft: defaults.footer.gutter.horizontal,\n  },\n  footerCount: {\n    color: defaults.footer.count.color,\n    fontSize: defaults.footer.count.fontSize,\n    paddingLeft: '1em', // add a small gutter for the caption\n  },\n  footerCaption: {\n    flex: '1 1 0',\n  }\n}\n\nexport default Footer\n"
  },
  {
    "path": "src/components/Header.js",
    "content": "import PropTypes from 'prop-types'\nimport React from 'react'\nimport { css, StyleSheet } from 'aphrodite/no-important'\n\nimport defaults from '../theme'\nimport { deepMerge } from '../utils/util'\nimport Icon from './Icon'\n\nfunction Header ({ customControls, onClose, showCloseBtn, closeBtnTitle, theme, ...props }) {\n  const classes = StyleSheet.create(deepMerge(defaultStyles, theme))\n\n  return (\n    <div className={css(classes.header)} {...props}>\n      {customControls ? customControls : <span />}\n      {!!showCloseBtn && (\n        <button\n          title={closeBtnTitle}\n          className={css(classes.close)}\n          onClick={onClose}\n        >\n          <Icon fill={!!theme.close && theme.close.fill || defaults.close.fill} type=\"close\" />\n        </button>\n      )}\n    </div>\n  )\n}\n\nHeader.propTypes = {\n  theme: PropTypes.object,\n  customControls: PropTypes.array,\n  onClose: PropTypes.func.isRequired,\n  showCloseBtn: PropTypes.bool,\n  closeBtnTitle: PropTypes.string,\n}\n\nconst defaultStyles = {\n  header: {\n    display: 'flex',\n    justifyContent: 'space-between',\n    height: defaults.header.height,\n  },\n  close: {\n    background: 'none',\n    border: 'none',\n    cursor: 'pointer',\n    outline: 'none',\n    position: 'relative',\n    top: 0,\n    verticalAlign: 'bottom',\n    zIndex: 1,\n\n    // increase hit area\n    height: 40,\n    marginRight: -10,\n    padding: 10,\n    width: 40,\n  }\n}\n\nexport default Header\n"
  },
  {
    "path": "src/components/Icon.js",
    "content": "import PropTypes from \"prop-types\";\nimport React from \"react\";\nimport arrowLeft from \"../icons/arrow_left\";\nimport arrowRight from \"../icons/arrow_right\";\nimport close from \"../icons/close\";\n\nconst icons = { arrowLeft, arrowRight, close };\n\nconst Icon = ({ fill, type, ...props }) => {\n  const icon = icons[type];\n\n  return <span dangerouslySetInnerHTML={{ __html: icon(fill) }} {...props} />;\n};\n\nIcon.propTypes = {\n  fill: PropTypes.string,\n  type: PropTypes.oneOf(Object.keys(icons)),\n};\nIcon.defaultProps = {\n  fill: \"#fff\",\n};\n\nexport default Icon;\n"
  },
  {
    "path": "src/components/PaginatedThumbnails.js",
    "content": "import PropTypes from 'prop-types'\nimport React, { Component } from 'react'\nimport { css, StyleSheet } from 'aphrodite/no-important'\n\nimport Thumbnail from './Thumbnail'\nimport Arrow from './Arrow'\nimport theme from '../theme'\n\nconst classes = StyleSheet.create({\n  paginatedThumbnails: {\n    bottom: theme.container.gutter.vertical,\n    height: theme.thumbnail.size,\n    padding: '0 50px',\n    position: 'absolute',\n    textAlign: 'center',\n    whiteSpace: 'nowrap',\n    left: '50%',\n    transform: 'translateX(-50%)',\n  }\n})\n\nconst arrowStyles = {\n  height: theme.thumbnail.size + (theme.thumbnail.gutter * 2),\n  width: 40,\n}\n\nexport default class PaginatedThumbnails extends Component {\n  constructor (props) {\n    super(props)\n\n    this.state = {\n      hasCustomPage: false,\n    }\n\n    this.gotoPrev = this.gotoPrev.bind(this)\n    this.gotoNext = this.gotoNext.bind(this)\n  }\n  UNSAFE_componentWillReceiveProps (nextProps) {\n    if (nextProps.currImg !== this.props.currImg) {\n      this.setState({\n        hasCustomPage: false,\n      })\n    }\n  }\n\n  // ====================\n  // Methods\n  // ====================\n\n  getFirst () {\n    const { currImg, offset } = this.props\n    if(this.state.hasCustomPage) {\n      return this.clampFirst(this.state.first)\n    }\n    return this.clampFirst(currImg - offset)\n  }\n  setFirst (event, newFirst) {\n    const { first } = this.state\n\n    if (event) {\n      event.preventDefault()\n      event.stopPropagation()\n    }\n\n    if (first === newFirst) return\n\n    this.setState({\n      hasCustomPage: true,\n      first: newFirst\n    })\n  }\n  gotoPrev(event) {\n    this.setFirst(event, this.getFirst() - this.props.offset)\n  }\n  gotoNext(event) {\n    this.setFirst(event, this.getFirst() + this.props.offset)\n  }\n  clampFirst (value) {\n    const { imgs, offset } = this.props\n\n    const totalCount = 2 * offset + 1 // show $offset extra thumbnails on each side\n\n    if (value < 0) {\n      return 0\n    } else if (value + totalCount > imgs.length) { // Too far\n      return imgs.length - totalCount\n    } else {\n      return value\n    }\n  }\n\n  // ====================\n  // Renderers\n  // ====================\n\n  renderArrowPrev (theme) {\n    const { leftTitle } = this.props\n    if (this.getFirst() <= 0) return null\n\n    return (\n      <Arrow\n        theme={theme}\n        direction=\"left\"\n        size=\"small\"\n        icon=\"arrowLeft\"\n        onClick={this.gotoPrev}\n        style={arrowStyles}\n        title={leftTitle}\n        type=\"button\"\n        />\n      )\n    }\n    renderArrowNext (theme) {\n      const { offset, imgs, rightTitle } = this.props\n      const totalCount = 2 * offset + 1\n      if (this.getFirst() + totalCount >= imgs.length) return null\n\n      return (\n        <Arrow\n        theme={theme}\n        direction=\"right\"\n        size=\"small\"\n        icon=\"arrowRight\"\n        onClick={this.gotoNext}\n        style={arrowStyles}\n        title={rightTitle}\n        type=\"button\"\n      />\n    )\n  }\n  render () {\n    const { imgs, currImg, onClickThumbnail, offset, theme } = this.props\n\n    const totalCount = 2 * offset + 1 // show $offset extra thumbnails on each side\n    let thumbnails = []\n    let baseOffset = 0\n    if (imgs.length <= totalCount) {\n      thumbnails = imgs\n    } else { // Try to center current image in list\n      baseOffset = this.getFirst()\n      thumbnails = imgs.slice(baseOffset, baseOffset + totalCount)\n    }\n\n    return (\n      <div className={css(classes.paginatedThumbnails)}>\n        {this.renderArrowPrev(theme)}\n        {thumbnails.map((img, idx) => (\n          <Thumbnail\n            theme={theme}\n            key={baseOffset + idx}\n            {...img}\n            index={baseOffset + idx}\n            onClick={onClickThumbnail}\n            active={baseOffset + idx === currImg}\n          />\n        ))}\n        {this.renderArrowNext(theme)}\n      </div>\n    )\n  }\n}\n\nPaginatedThumbnails.propTypes = {\n  theme: PropTypes.object,\n  leftTitle: PropTypes.string,\n  rightTitle: PropTypes.string,\n  currImg: PropTypes.number,\n  imgs: PropTypes.array,\n  offset: PropTypes.number,\n  onClickThumbnail: PropTypes.func.isRequired,\n}\n"
  },
  {
    "path": "src/components/Portal.js",
    "content": "import React, { Component } from 'react'\nimport PropsTypes from 'prop-types'\nimport { CSSTransition, TransitionGroup } from 'react-transition-group'\nimport { render, unmountComponentAtNode } from 'react-dom'\n\n\nexport default class Portal extends Component {\n  constructor() {\n    super()\n    this.portalElement = null\n  }\n  componentDidMount() {\n    const div = document.createElement('div')\n    document.body.appendChild(div)\n    this.portalElement = div\n    this.componentDidUpdate()\n  }\n  componentDidUpdate() {\n    // Animate fade on mount/unmount\n    const duration = 200\n    const styles = `\n\t\t\t\t.fade-enter { opacity: 0.01; }\n\t\t\t\t.fade-enter.fade-enter-active { opacity: 1; transition: opacity ${duration}ms; }\n\t\t\t\t.fade-leave { opacity: 1; }\n\t\t\t\t.fade-leave.fade-leave-active { opacity: .01; transition: opacity ${duration}ms; }\n\t\t`\n\n    render(\n      <div>\n        <style>{styles}</style>\n        <TransitionGroup\n          {...this.props}>\n          <CSSTransition timeout={{ enter: duration, exit: duration }} className=\"fade\">\n            <div>{this.props.children}</div>\n          </CSSTransition>\n        </TransitionGroup>\n      </div>,\n      this.portalElement\n    )\n  }\n  componentWillUnmount() {\n    unmountComponentAtNode(this.portalElement)\n    document.body.removeChild(this.portalElement)\n  }\n  render() {\n    return null\n  }\n}\n\nPortal.propTypes = {\n  children: PropsTypes.arrayOf(PropsTypes.any),\n}\n"
  },
  {
    "path": "src/components/Spinner.js",
    "content": "import PropTypes from 'prop-types'\nimport React from 'react'\nimport { css, StyleSheet } from 'aphrodite/no-important'\n\nconst Spinner = props => {\n  const classes = StyleSheet.create(styles(props))\n\n  return (\n    <div className={css(classes.bouncingLoader)}>\n      <div className={css(classes.child)} />\n      <div className={css(classes.child, classes.child2)} />\n      <div className={css(classes.child, classes.child3)} />\n    </div>\n  )\n}\n\nSpinner.propTypes = {\n  color: PropTypes.string,\n  size: PropTypes.number,\n}\n\nconst bouncingKeyframes = (size) => ({\n  '0%': {\n    opacity: 1,\n    transform: 'translateY(0)',\n  },\n  '100%': {\n    opacity: .1,\n    transform: `translateY(-${size}px)`,\n  }\n})\n\nconst styles = ({ color, size }) => ({\n  bouncingLoader: {\n    display: 'flex',\n    justifyContent: 'center',\n  },\n  child: {\n    width: size,\n    height: size,\n    margin: `${3 * size}px ${ .2 * size}px`,\n    background: color,\n    borderRadius: '50%',\n    animationName: bouncingKeyframes(size),\n    animationDuration: '.6s',\n    animationDirection: 'alternate',\n    animationIterationCount: 'infinite',\n  },\n  child2: {\n    animationDelay: '0.2s',\n  },\n  child3: {\n    animationDelay: '0.4s',\n  }\n})\n\nexport default Spinner\n"
  },
  {
    "path": "src/components/Thumbnail.js",
    "content": "import PropTypes from 'prop-types'\nimport React from 'react'\nimport { css, StyleSheet } from 'aphrodite/no-important'\n\nimport defaults from '../theme'\nimport { deepMerge } from '../utils/util'\n\n\nfunction Thumbnail ({ index, src, thumbnail, active, onClick, theme }) {\n  const url = thumbnail || src\n  const classes = StyleSheet.create(deepMerge(defaultStyles, theme))\n\n  return (\n    <div\n      className={css(classes.thumbnail, active && classes.thumbnail__active)}\n      onClick={(e) => {\n        e.preventDefault()\n        e.stopPropagation()\n        onClick(index)\n      }}\n      style={{ backgroundImage: `url(\"${url}\")` }}\n    />\n  )\n}\n\nThumbnail.propTypes = {\n  theme: PropTypes.object,\n  active: PropTypes.bool,\n  index: PropTypes.number,\n  onClick: PropTypes.func.isRequired,\n  src: PropTypes.string,\n  thumbnail: PropTypes.string,\n}\n\nconst defaultStyles = {\n  thumbnail: {\n    backgroundPosition: 'center',\n    backgroundSize: 'cover',\n    borderRadius: 2,\n    boxShadow: 'inset 0 0 0 1px hsla(0, 0%, 100%, .2)',\n    cursor: 'pointer',\n    display: 'inline-block',\n    height: defaults.thumbnail.size,\n    margin: defaults.thumbnail.gutter,\n    overflow: 'hidden',\n    width: defaults.thumbnail.size,\n  },\n  thumbnail__active: {\n    boxShadow:`inset 0 0 0 2px ${defaults.thumbnail.activeBorderColor}`,\n  }\n}\n\nexport default Thumbnail\n"
  },
  {
    "path": "src/components/Thumbnails.js",
    "content": "import PropTypes from 'prop-types'\nimport React from 'react'\nimport { css, StyleSheet } from 'aphrodite/no-important'\nimport Thumbnail from './Thumbnail'\nimport defaults from '../theme'\n\nfunction Thumbnails ({ currImg, imgs, onClickThumbnail }) {\n  return (\n    <div className={css(classes.thumbnail)}>\n      {imgs.map((img, idx) => {\n        <Thumbnail\n          {...img}\n          active={idx === currImg}\n          index={idx}\n          key={idx}\n          onClick={onClickThumbnail}\n        />\n      })}\n    </div>\n  )\n}\n\nThumbnails.propTypes = {\n  currImg: PropTypes.number,\n  imgs: PropTypes.array,\n  onClickThumbnail: PropTypes.func.isRequired,\n}\n\nconst classes = StyleSheet.create({\n  Thumbnails: {\n    bottom: defaults.container.gutter.vertical,\n    color: '#fff',\n    height: defaults.thumbnail.height,\n    left: defaults.container.gutter.horizontal,\n    overflowX: 'scroll',\n    overflowY: 'hidden',\n    position: 'absolute',\n    right: defaults.container.gutter.horizontal,\n    textAlign: 'center',\n    whiteSpace: 'nowrap',\n  }\n})\n\nexport default Thumbnails\n"
  },
  {
    "path": "src/icons/Close.js",
    "content": "export default (fill) => (\n\t`<svg fill=\"${fill}\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\" width=\"100%\" height=\"100%\" viewBox=\"0 0 512 512\" style=\"enable-background:new 0 0 512 512;\" xml:space=\"preserve\">\n\t\t<path d=\"M443.6,387.1L312.4,255.4l131.5-130c5.4-5.4,5.4-14.2,0-19.6l-37.4-37.6c-2.6-2.6-6.1-4-9.8-4c-3.7,0-7.2,1.5-9.8,4 L256,197.8L124.9,68.3c-2.6-2.6-6.1-4-9.8-4c-3.7,0-7.2,1.5-9.8,4L68,105.9c-5.4,5.4-5.4,14.2,0,19.6l131.5,130L68.4,387.1 c-2.6,2.6-4.1,6.1-4.1,9.8c0,3.7,1.4,7.2,4.1,9.8l37.4,37.6c2.7,2.7,6.2,4.1,9.8,4.1c3.5,0,7.1-1.3,9.8-4.1L256,313.1l130.7,131.1 c2.7,2.7,6.2,4.1,9.8,4.1c3.5,0,7.1-1.3,9.8-4.1l37.4-37.6c2.6-2.6,4.1-6.1,4.1-9.8C447.7,393.2,446.2,389.7,443.6,387.1z\"/>\n\t</svg>`\n);\n"
  },
  {
    "path": "src/icons/arrow_left.js",
    "content": "export default (fill) => (\n  `<svg fill=\"${fill}\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\" width=\"100%\" height=\"100%\" viewBox=\"0 0 512 512\" xml:space=\"preserve\">\n\t\t<path d=\"M213.7,256L213.7,256L213.7,256L380.9,81.9c4.2-4.3,4.1-11.4-0.2-15.8l-29.9-30.6c-4.3-4.4-11.3-4.5-15.5-0.2L131.1,247.9 c-2.2,2.2-3.2,5.2-3,8.1c-0.1,3,0.9,5.9,3,8.1l204.2,212.7c4.2,4.3,11.2,4.2,15.5-0.2l29.9-30.6c4.3-4.4,4.4-11.5,0.2-15.8 L213.7,256z\"/>\n\t</svg>`\n)\n"
  },
  {
    "path": "src/icons/arrow_right.js",
    "content": "export default (fill) => (\n  `<svg fill=\"${fill}\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\" width=\"100%\" height=\"100%\" viewBox=\"0 0 512 512\" xml:space=\"preserve\">\n\t\t<path d=\"M298.3,256L298.3,256L298.3,256L131.1,81.9c-4.2-4.3-4.1-11.4,0.2-15.8l29.9-30.6c4.3-4.4,11.3-4.5,15.5-0.2l204.2,212.7 c2.2,2.2,3.2,5.2,3,8.1c0.1,3-0.9,5.9-3,8.1L176.7,476.8c-4.2,4.3-11.2,4.2-15.5-0.2L131.3,446c-4.3-4.4-4.4-11.5-0.2-15.8 L298.3,256z\"/>\n\t</svg>`\n);\n"
  },
  {
    "path": "src/icons/close.js",
    "content": "export default (fill) => (\n\t`<svg fill=\"${fill}\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\" width=\"100%\" height=\"100%\" viewBox=\"0 0 512 512\" style=\"enable-background:new 0 0 512 512;\" xml:space=\"preserve\">\n\t\t<path d=\"M443.6,387.1L312.4,255.4l131.5-130c5.4-5.4,5.4-14.2,0-19.6l-37.4-37.6c-2.6-2.6-6.1-4-9.8-4c-3.7,0-7.2,1.5-9.8,4 L256,197.8L124.9,68.3c-2.6-2.6-6.1-4-9.8-4c-3.7,0-7.2,1.5-9.8,4L68,105.9c-5.4,5.4-5.4,14.2,0,19.6l131.5,130L68.4,387.1 c-2.6,2.6-4.1,6.1-4.1,9.8c0,3.7,1.4,7.2,4.1,9.8l37.4,37.6c2.7,2.7,6.2,4.1,9.8,4.1c3.5,0,7.1-1.3,9.8-4.1L256,313.1l130.7,131.1 c2.7,2.7,6.2,4.1,9.8,4.1c3.5,0,7.1-1.3,9.8-4.1l37.4-37.6c2.6-2.6,4.1-6.1,4.1-9.8C447.7,393.2,446.2,389.7,443.6,387.1z\"/>\n\t</svg>`\n);\n"
  },
  {
    "path": "src/theme.js",
    "content": "// ===================\n// Theme\n// ===================\n\nconst theme = {}\n\n// container\ntheme.container = {\n  background: 'rgba(0, 0, 0, .8)',\n  gutter: {\n    horizontal: 10,\n    vertical: 10,\n  },\n  zIndex: 2001\n}\n\n// header\ntheme.header = {\n  height: 40,\n}\ntheme.close = {\n  fill: 'white'\n}\n\n// footer\ntheme.footer = {\n  color: '#fff',\n  count: {\n    color: 'rgba(255, 255, 255, .75)',\n    fontSize: '.85em'\n  },\n  height: 40,\n  gutter: {\n    horizontal: 0,\n    vertical: 5,\n  }\n}\n\n// thumbnails\ntheme.thumbnail = {\n  activeBorderColor: '#fff',\n  size: 50,\n  gutter: 2,\n}\n\n// arrow\ntheme.arrow = {\n  background: 'none',\n  fill: '#fff',\n  height: 120,\n}\n\n\nexport default theme\n"
  },
  {
    "path": "src/utils/constant.js",
    "content": "// obj\nexport const OBJ_KEYS = Object.keys\n"
  },
  {
    "path": "src/utils/util.js",
    "content": "export function deepMerge(source, target = {}) {\n  const extended = Object.assign({}, target);\n\n  Object.keys(source).forEach((key) => {\n    if (typeof source[key] !== \"object\" || !source[key]) {\n      extended[key] = source[key];\n    } else {\n      if (!target[key]) {\n        extended[key] = source[key];\n      } else {\n        extended[key] = deepMerge(target[key], source[key]);\n      }\n    }\n  });\n  return extended;\n}\n// export function deepMerge(source, target = {}) {\n//   // initialize with source styles\n//   const styles = { ...source }\n\n//   // massage in target styles\n//   Object.keys(target).forEach(key => {\n//     if (source[key]) {\n//       styles[key] = (rsCss, props) => {\n//         return target[key](source[key](rsCss, props), props)\n//       }\n//     } else {\n//       styles[key] = target[key]\n//     }\n//   })\n\n//   return styles\n// }\n\nexport const canUseDom = !!(\n  typeof window !== \"undefined\" &&\n  window.document &&\n  window.document.createElement\n);\n\n/**\n * Bind multiple conponent methods:\n * @param {this} context\n * @param {Array} functions\n *\n * constructor() {\n *   ...\n *   bindFunctions.call(this, ['handleClick', 'handleOther'])\n * }\n */\nexport function bindFunctions(functions) {\n  functions.forEach((f) => (this[f] = this[f].bind(this)));\n}\n"
  },
  {
    "path": "webpack.config.js",
    "content": "const path = require(\"path\");\nconst HtmlWebpackPlugin = require(\"html-webpack-plugin\");\nconst MiniCssExtractPlugin = require(\"mini-css-extract-plugin\");\n\nmodule.exports = {\n  mode: \"development\",\n  context: path.resolve(__dirname, \"examples/src\"),\n  entry: {\n    app: \"./app.js\",\n    app_CN: \"./app_CN.js\",\n    common: [\"react-images-viewer\"],\n  },\n  output: {\n    path: path.resolve(__dirname, \"examples/dist\"),\n    filename: \"[name].js\",\n    publicPath: \"/\",\n  },\n  devtool: \"inline-source-map\",\n  devServer: {\n    contentBase: path.resolve(__dirname, \"examples/src\"),\n    host: \"0.0.0.0\",\n    port: 8001,\n  },\n  module: {\n    rules: [\n      {\n        test: /\\.js$/,\n        exclude: [/node_modules/],\n        use: [\n          {\n            loader: \"babel-loader\",\n            options: { presets: [\"react\", \"env\"] },\n          },\n        ],\n      },\n      {\n        test: /\\.less$/,\n        use: [MiniCssExtractPlugin.loader, \"css-loader\", \"less-loader\"],\n      },\n      {\n        test: /\\.html$/,\n        use: [\n          {\n            loader: \"html-loader\",\n          },\n        ],\n      },\n    ],\n  },\n  resolve: {\n    alias: {\n      \"react-images-viewer\": path.resolve(__dirname, \"src/ImgsViewer\"),\n    },\n  },\n  optimization: {\n    splitChunks: {\n      name: true,\n      // filename: 'common.js',\n      // minChunks: 2,\n      cacheGroups: {\n        common: {\n          test: /[\\\\/]node_modules[\\\\/]/,\n          name: \"common\",\n          chunks: \"all\",\n        },\n      },\n    },\n  },\n  plugins: [\n    new HtmlWebpackPlugin({\n      filename: \"index.html\",\n      inject: false,\n      template: path.resolve(__dirname, \"examples/src/index.html\"),\n    }),\n    new HtmlWebpackPlugin({\n      filename: \"index_CN.html\",\n      inject: false,\n      template: path.resolve(__dirname, \"examples/src/index_CN.html\"),\n    }),\n    new MiniCssExtractPlugin({\n      filename: \"example.css\",\n      chunkFilename: \"example.css\",\n    }),\n  ],\n};\n"
  }
]