Skip to content

Commit

Permalink
feat(ImageGallery): adds ImageGallery and ImageGalleryImage component (
Browse files Browse the repository at this point in the history
…#315)

* chore: checking in progress work

* chore:check in progress

* chore: check in progress

* feature: adds imageGallery and ImageGalleryImage to create gallery view for images

* chore: add comment explaining eslint-disable-next-line

* chore: style changes to fix butotn click space

* chore: remove unnecessary images from static folder

* chore: remove unnecessary packages

* chore: remove accidentally added gallery page

* chore: add tokens and fix mobile view

* refactor: add Row to ImageGallery

* docs: update imagegallery.mdx

* chore: adds focus outline to ImageGallery and check to verify type of element

* chore: fixes addNoScroll staying, adds color token and adds a11y annotation

* chore: removes margin on AddNoScroll, scopes column changes, adds dark mode

* chore: add margin to image

* chore: remove dark theme

* chore: center image and restyle title

* refactor: adds markdown image ability, updates styles, fixes firefox issue, and updates image gallery mdx

* chore: remove comments

* chore: adds styling to remove outline and change margins

* chore: updates margins on arrows, close button, and title position
  • Loading branch information
abbeyhrt authored and alisonjoseph committed Aug 21, 2019
1 parent 1fc4b98 commit eb31926
Show file tree
Hide file tree
Showing 20 changed files with 610 additions and 0 deletions.
2 changes: 2 additions & 0 deletions packages/example/src/data/nav-items.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,5 @@
path: /components/Tabs
- title: Video
path: /components/Video
- title: Image Gallery
path: /components/ImageGallery
131 changes: 131 additions & 0 deletions packages/example/src/pages/components/ImageGallery.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
---
title: Image Gallery
---

<PageDescription>

A small scale example of the Image Gallery from the [IBM Design Language Gallery](https://www.ibm.com/design/language/gallery).

</PageDescription>

## Image Gallery Example

Click on an image to open the gallery.

<ImageGallery>

<ImageGalleryImage alt="IBM Design" title="IBM Design" col={4}>

![IBM Design](/images/IBM_Design_landing.jpg)

</ImageGalleryImage>

<ImageGalleryImage alt="IBM Cloud Logo" title="IBM Cloud" col={4}>

![IBM Cloud Logo](/images/IBM_Cloud_Logo.png)

</ImageGalleryImage>
<ImageGalleryImage alt="IBM Cloud Developers" title="IBM Cloud Developers" col={4}>

![IBM Cloud Developers](/images/IBM_Cloud_Developers.jpg)

</ImageGalleryImage>
<ImageGalleryImage alt="IBM Cloud Data Center" title="IBM Cloud Data Center" col={8}>

![IBM Cloud Data Center](/images/IBM_Cloud_Data_Center.jpg)

</ImageGalleryImage>
<ImageGalleryImage alt="IBM Cloud Interconnect" title="IBM Cloud Interconnect" col={4}>

![IBM Cloud Interconnect](/images/IBM_Cloud_Interconnect.jpg)

</ImageGalleryImage>
<ImageGalleryImage alt="IBM Cloud Notebook" title="IBM Cloud Notebook" col={4}>

![IBM Cloud Notebook](/images/IBM_Cloud_Notebook.jpg)

</ImageGalleryImage>
<ImageGalleryImage alt="IBM Cloud Platform Prototype" title="IBM Cloud Platform" col={8}>

![IBM Cloud Platform Prototype](/images/IBM_Cloud_Platform_Prototype.gif)

</ImageGalleryImage>
<ImageGalleryImage alt="IBM Cloud Pictograms" title="IBM Cloud Pictograms" col={4}>

![IBM Cloud Pictograms](/images/IBM_Cloud_Pictograms.gif)

</ImageGalleryImage>
<ImageGalleryImage alt="IBM Cloud Server" title="IBM Cloud Server" col={4}>

![IBM Cloud Server](/images/IBM_Cloud_Server.png)

</ImageGalleryImage>
<ImageGalleryImage alt="IBM Cloud Think" title="IBM Cloud Think" col={4}>

![IBM Cloud Think](/images/IBM_Cloud_Think_Keynote.jpg)

</ImageGalleryImage>

</ImageGallery>

## Markdown Code

You can use the ImageGallery component in markdown by nesting your images inside the ImageGallery component and using the ImageGalleryImage component to define the image's location, title, alt tag, and the columns the image on the page will span at the medium and large breakpoints. There is no gallery view for mobile so the small breakpoint is not defined.

Here's an example of how to use the ImageGallery and the ImageGalleryImage components in markdown.

```markdown path=/components/ImageGallery.mdx src=https://gatsby-theme-carbon.now.sh
<ImageGallery>
<ImageGalleryImage alt="IBM Design" title="IBM Design" col={4}>

![IBM Design](/images/IBM_Design_landing.jpg)

</ImageGalleryImage>

<ImageGalleryImage alt="IBM Cloud Logo" title="IBM Cloud" col={4}>

![IBM Cloud Logo](/images/IBM_Cloud_Logo.png)

</ImageGalleryImage>
<ImageGalleryImage alt="IBM Cloud Developers" title="IBM Cloud Developers" col={4}>

![IBM Cloud Developers](/images/IBM_Cloud_Developers.jpg)

</ImageGalleryImage>
<ImageGalleryImage alt="IBM Cloud Data Center" title="IBM Cloud Data Center" col={8}>

![IBM Cloud Data Center](/images/IBM_Cloud_Data_Center.jpg)

</ImageGalleryImage>
<ImageGalleryImage alt="IBM Cloud Interconnect" title="IBM Cloud Interconnect" col={4}>

![IBM Cloud Interconnect](/images/IBM_Cloud_Interconnect.jpg)

</ImageGalleryImage>
<ImageGalleryImage alt="IBM Cloud Notebook" title="IBM Cloud Notebook" col={4}>

![IBM Cloud Notebook](/images/IBM_Cloud_Notebook.jpg)

</ImageGalleryImage>
<ImageGalleryImage alt="IBM Cloud Platform Prototype" title="IBM Cloud Platform" col={8}>

![IBM Cloud Platform Prototype](/images/IBM_Cloud_Platform_Prototype.gif)

</ImageGalleryImage>
<ImageGalleryImage alt="IBM Cloud Pictograms" title="IBM Cloud Pictograms" col={4}>

![IBM Cloud Pictograms](/images/IBM_Cloud_Pictograms.gif)

</ImageGalleryImage>
<ImageGalleryImage alt="IBM Cloud Server" title="IBM Cloud Server" col={4}>

![IBM Cloud Server](/images/IBM_Cloud_Server.png)

</ImageGalleryImage>
<ImageGalleryImage alt="IBM Cloud Think" title="IBM Cloud Think" col={4}>

![IBM Cloud Think](/images/IBM_Cloud_Think_Keynote.jpg)

</ImageGalleryImage>
</ImageGallery>
```
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
17 changes: 17 additions & 0 deletions packages/example/static/images/npm.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions packages/gatsby-theme-carbon/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"classnames": "^2.2.6",
"copy-to-clipboard": "^3.2.0",
"emotion-theming": "^10.0.10",
"focus-trap-react": "^6.0.0",
"gatsby-plugin-catch-links": "^2.0.15",
"gatsby-plugin-emotion": "^4.0.7",
"gatsby-plugin-manifest": "^2.1.1",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
import React, { useState, useEffect, Children } from 'react';
import ReactDOM from 'react-dom';
import { breakpoints } from '@carbon/elements';
import { ChevronRight32, ChevronLeft32, Close32 } from '@carbon/icons-react';
import cx from 'classnames';
import FocusTrap from 'focus-trap-react';
import useMedia from 'use-media';
import PropTypes from 'prop-types';
import { Grid, Row, Column } from '../Grid';
import {
galleryContainer,
inDialogGalleryContainer,
galleryGrid,
galleryRow,
navButtons,
closeButton,
icon,
navButtonsContainer,
firstRightNav,
rightNav,
leftNav,
addNoScroll,
} from './ImageGallery.module.scss';

function ImageGallery({ children }) {
const [portalsNode, updateNode] = useState(null);
const [isGalleryOpen, setIsGalleryOpen] = useState(false);
const [activeImageIndex, updateActiveImageIndex] = useState(null);
const childrenAsArray = Children.toArray(children);
const rightNavButton = cx({
[rightNav]: true,
[firstRightNav]: activeImageIndex === 0,
[navButtons]: activeImageIndex > 0,
});
const leftNavButton = cx([leftNav], [navButtons]);
const isMobile = useMedia({ maxWidth: breakpoints.md.width });

// Creates the node to go into the portalsNode state.
useEffect(() => {
const node = document.createElement('div');
document.body.appendChild(node);
updateNode(node);

return () => {
node.parentNode.removeChild(node);
};
}, []);

// Depending on if the gallery is open or not, this adds the addNoScroll class so the screen behind the modal doesn't scroll when opened.
useEffect(() => {
if (isGalleryOpen) {
document.body.classList.add(addNoScroll);
}

return () => {
document.body.classList.remove(addNoScroll);
};
}, [isGalleryOpen]);

// Removes addNoScroll if view is shrunk to mobile view when the gallery is open
useEffect(() => {
if (
(isMobile && document.body.classList.contains(addNoScroll)) ||
!isGalleryOpen
) {
document.body.classList.remove(addNoScroll);
}

return () => {
if (!isMobile && isGalleryOpen) {
document.body.classList.add(addNoScroll);
}
};
}, [isGalleryOpen, isMobile]);

// Opens gallery if the breakpoint isn't mobile
function openGalleryForImage(index) {
return () => {
if (!isMobile) {
setIsGalleryOpen(true);
updateActiveImageIndex(index);
}
};
}

function closeGallery() {
setIsGalleryOpen(false);
updateActiveImageIndex(null);
}

function selectNextImage() {
if (activeImageIndex + 1 < childrenAsArray.length) {
updateActiveImageIndex(activeImageIndex + 1);
}
}

function selectPrevImage() {
if (activeImageIndex - 1 >= 0) {
updateActiveImageIndex(activeImageIndex - 1);
}
}

function onKeyDown(event) {
if (event.key === 'Escape') {
closeGallery();
return;
}
if (event.key === 'ArrowLeft') {
selectPrevImage();
return;
}
if (event.key === 'ArrowRight') {
selectNextImage();
}
}

return (
<>
<figure role="group" aria-label="Gallery of Various Media">
<Row className={galleryContainer}>
{Children.map(children, (child, index) =>
React.cloneElement(child, {
onClick: openGalleryForImage(index),
})
)}
</Row>
</figure>
{portalsNode &&
isGalleryOpen &&
!isMobile &&
ReactDOM.createPortal(
<FocusTrap>
{/* Because of FocusTrap, the key down events will propagate up removing the accessibility problem that would be created by having a keydown event listener on a non-interactive element. */}
{/* eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions */}
<div
role="group"
className={inDialogGalleryContainer}
onKeyDown={onKeyDown}
>
<Row>
<Column colLg={2}>
<button
type="button"
className={closeButton}
onClick={closeGallery}
>
<Close32 className={icon} />
</button>
</Column>
</Row>
<Grid className={cx('bx--grid--full-width', galleryGrid)}>
<Row className={galleryRow}>
<Column colLg={3} className={navButtonsContainer}>
{activeImageIndex - 1 >= 0 && (
<button
type="button"
className={leftNavButton}
onClick={selectPrevImage}
>
<ChevronLeft32 className={icon} />
</button>
)}
</Column>
<Column colLg={6}>
{React.cloneElement(childrenAsArray[activeImageIndex], {
isInDialog: true,
})}
</Column>
<Column colLg={3} className={navButtonsContainer}>
{activeImageIndex + 1 < childrenAsArray.length && (
<button
type="button"
className={rightNavButton}
onClick={selectNextImage}
>
<ChevronRight32 className={icon} />
</button>
)}
</Column>
</Row>
</Grid>
</div>
</FocusTrap>,
portalsNode
)}
</>
);
}

ImageGallery.propTypes = {
children: PropTypes.arrayOf(PropTypes.element).isRequired,
};

export default ImageGallery;
Loading

1 comment on commit eb31926

@vercel
Copy link

@vercel vercel bot commented on eb31926 Aug 21, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.