Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
683e76f
feat: shadowing Minicard component
leonardo-costa-IBM Oct 27, 2025
5ea75e4
feat(ui-icons): update contribute tab content
leonardo-costa-IBM Oct 27, 2025
c1f63ab
feat(app-icons): update contribute tab content
leonardo-costa-IBM Oct 27, 2025
8161689
feat(pictograms): update contribute tab content
leonardo-costa-IBM Oct 27, 2025
03a5398
Added new overview page and navigation (#1476)
Dan-Pastrolin-IBM Oct 29, 2025
d6d7410
feat(iconography): update contribute tab on all sections
leonardo-costa-IBM Nov 3, 2025
6ca2273
feat(iconography): update the NoResult component for all sections
leonardo-costa-IBM Nov 3, 2025
7ae8bc8
feat(app-icons): update design tab imagery & content
leonardo-costa-IBM Nov 4, 2025
e15491c
feat(ui-icons): updated design page and its assets
Dan-Pastrolin-IBM Nov 4, 2025
0dba567
feat(ui-icons): rearanged assets and src for design page
Dan-Pastrolin-IBM Nov 4, 2025
4e63e86
feat(ui-icons): update entire usage page content and assets
Dan-Pastrolin-IBM Nov 4, 2025
3fd6df4
feat(carousel): component rework + changed ui-icons usage Tabs to new…
Dan-Pastrolin-IBM Nov 4, 2025
8c388c9
feat(app-icons): update usage tab imagery & content
leonardo-costa-IBM Nov 4, 2025
af46a06
feat(pictograms): update design tab imagery & content
leonardo-costa-IBM Nov 4, 2025
ce5ce0f
feat(pictogram): update usage tab imagery & content
leonardo-costa-IBM Nov 4, 2025
92432fc
feat(no-result): edited text conditional to render text and hyperlink…
Dan-Pastrolin-IBM Nov 4, 2025
c9724cb
feat(iconography): minor content updates on Contribute tabs
leonardo-costa-IBM Nov 6, 2025
a1742a9
feat(iconography): update resource links on Design tabs
leonardo-costa-IBM Nov 6, 2025
433188b
feat(multiple): copy and assets changes after design review
Dan-Pastrolin-IBM Nov 7, 2025
a6a3bec
fix(pictogram): edited foundation paragraph
Dan-Pastrolin-IBM Nov 7, 2025
af4160c
feat(pictograms): minor updates on Design tab content
leonardo-costa-IBM Nov 10, 2025
8f531d7
fix(icon-library): changed page name to UI icon
Dan-Pastrolin-IBM Nov 10, 2025
8fe6419
feat(multiple): edited pages due to design review
Dan-Pastrolin-IBM Nov 11, 2025
707c864
fix(multiple): applied design review fixes and updates
Dan-Pastrolin-IBM Nov 13, 2025
8de20e7
style: remove padding from Tab component
leonardo-costa-IBM Nov 13, 2025
43b0fe6
fix(pictograms): added period to caption
Dan-Pastrolin-IBM Nov 13, 2025
5eb6e15
fix(app-contribute): edited copy on prod-ready section
Dan-Pastrolin-IBM Nov 13, 2025
07c93f0
feat(app-icons > contribute): copy updates
leonardo-costa-IBM Nov 25, 2025
afd666f
feat(pictograms > contribute): copy updates
leonardo-costa-IBM Nov 25, 2025
b722925
feat(app-icons > usage): copy & imagery updates
leonardo-costa-IBM Nov 25, 2025
4bd260f
feat(multiple): update library searcher component
leonardo-costa-IBM Nov 25, 2025
a696b40
feat(iconography): design review updates to pages overview and ui-ico…
Dan-Pastrolin-IBM Nov 25, 2025
c4e786f
feat(multiple): updated assets on overview and ui-icons design
Dan-Pastrolin-IBM Nov 25, 2025
3edb07b
feat(ui-icons): update NoResults copy
leonardo-costa-IBM Nov 27, 2025
6371c20
feat(pictograms > usage & design): update copies & imagery
leonardo-costa-IBM Nov 27, 2025
5bd3aa8
feat(multiple): updated iconography pages as reviewed by designers
Dan-Pastrolin-IBM Nov 27, 2025
710118b
feat(ui-icons > contribute): update section title
leonardo-costa-IBM Nov 28, 2025
3bcf4a5
fix(searcher-component): typo
leonardo-costa-IBM Nov 28, 2025
781cde9
feat(pictograms > contribute): update section copy
leonardo-costa-IBM Nov 28, 2025
b3f6133
fix(search-component): typo
leonardo-costa-IBM Nov 28, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
334 changes: 111 additions & 223 deletions src/components/Carousel/Carousel.js
Original file line number Diff line number Diff line change
@@ -1,241 +1,129 @@
/* eslint-disable no-lonely-if */
import React from 'react';
import PropTypes from 'prop-types';
import { RadioButtonGroup, RadioButton } from 'carbon-components-react';
import { settings } from 'carbon-components';
import React, { useState, useRef } from "react";

const { prefix } = settings;
import { Button } from "carbon-components-react";

let AP_ID;
import CaretLeft from "@carbon/icons-react/lib/CaretLeft";
import CaretRight from "@carbon/icons-react/lib/CaretRight";

export default class Carousel extends React.Component {
initialX = null;
import {
carouselWrapper,
slidesWrapper,
slidesNavigation,
slidesCounter,
scrollAnchor,
} from "./Carousel.module.scss";

initialY = null;
const Carousel = ({ children, carouselLabel, ...rest }) => {
const carouselId = useRef(Date.now());
const [activeSlideIndex, setActiveSlideIndex] = useState(0);
const slidesNum = React.Children.toArray(children).length - 1;
const carouselScrollToRef = useRef(null);

static propTypes = {
/**
* for slide images
*/
children: PropTypes.node,
/**
* unique id for each carousel, required
*/
id: PropTypes.string.isRequired,
/**
* value of each radio button starting from 1 to however many slides
* separated by a space
* i.e. "1 2 3 4"
*/
count: PropTypes.string.isRequired,
};

constructor(props) {
super(props);

this.onChange = this.onChange.bind(this);

const stringArr = this.props.count.split(' ');
const numArr = stringArr.map(i => Number(i));

this.state = {
checkedRadio: 1,
autoplay: !!this.props.autoPlay,
items: numArr,
};
}
console.log(`Children List: ${React.Children.toArray(children)}`)

componentDidMount() {
if (typeof document !== 'undefined') {
const slide = document.querySelector(
`.${prefix}--carousel-slide.${this.props.id}`
);
slide.addEventListener('touchstart', this.touchStart, false);
slide.addEventListener('touchmove', this.touchMove, false);
slide.addEventListener('mousedown', this.mouseStart);
slide.addEventListener('mousemove', this.mouseMove);
if (this.state.autoplay) AP_ID = setInterval(this.nextSlide, 6000);
}
}

componentWillUnmount() {
clearInterval(AP_ID);
}

// TOUCH EVENT HANDLERS
touchStart = e => {
clearInterval(AP_ID);
this.setState({
autoplay: false,
});
this.initialX = e.touches[0].clientX;
this.initialY = e.touches[0].clientY;
};
const manageActiveSlide = (direction) => {
setActiveSlideIndex((prevState) => {
const requestedIndex = direction === "up" ? prevState + 1 : prevState - 1;
let nextIndex = requestedIndex;

touchMove = e => {
if (this.initialX === null) {
return;
}

if (this.initialY === null) {
return;
}

const currentX = e.touches[0].clientX;
const currentY = e.touches[0].clientY;
const diffX = this.initialX - currentX;
const diffY = this.initialY - currentY;
const state = this.state.checkedRadio;
const { items } = this.state;

if (Math.abs(diffX) > Math.abs(diffY)) {
if (diffX > 0) {
// swiped left
if (state === items.length) {
this.onChange(items[0]);
} else {
this.onChange(state + 1);
}
} else {
// swiped right
if (state === items[0]) {
this.onChange(items.length);
} else {
this.onChange(state - 1);
}
if (direction === "up" && nextIndex > slidesNum) {
nextIndex = prevState;
}
}

this.initialX = null;
this.initialY = null;
e.preventDefault();
};

// MOUSE EVENT HANDLERS
mouseStart = e => {
clearInterval(AP_ID);
this.setState({
autoplay: false,
});
this.initialX = e.clientX;
this.initialY = e.clientY;
};

mouseMove = e => {
if (this.initialX === null) {
return;
}

if (this.initialY === null) {
return;
}
const finalX = e.clientX;
const finalY = e.clientY;
const diffX = this.initialX - finalX;
const diffY = this.initialY - finalY;
const state = this.state.checkedRadio;
const { items } = this.state;

if (Math.abs(diffX) > Math.abs(diffY)) {
if (diffX > 0) {
// swiped left
if (state === items.length) {
this.onChange(items[0]);
} else {
this.onChange(state + 1);
}
} else {
// swiped right
if (state === items[0]) {
this.onChange(items.length);
} else {
this.onChange(state - 1);
}
if (direction === "down" && nextIndex <= 0) {
nextIndex = 0;
}
this.initialX = null;
this.initialY = null;
e.preventDefault();
}
};

// AUTOPLAY FUNC.
nextSlide = () => {
const state = this.state.checkedRadio;
const { items } = this.state;
if (state === items.length) {
this.onChange(items[0]);
} else {
this.onChange(state + 1);
}
};

// UPDATING RADIO BUTTON
onChange = e => {
if (typeof document !== 'undefined') {
const slide = document.querySelector(
`.${prefix}--carousel-slide.${this.props.id}`
);
const images = slide.querySelectorAll('img');
if (carouselScrollToRef.current) {
const windowY = window.scrollY;
const elementRect = carouselScrollToRef.current.getBoundingClientRect();
const elementY = elementRect.top + windowY;

this.setState({
checkedRadio: e,
});

images.forEach((img, i) => {
if (this.props.fade) {
img.style.zIndex = i + 1 === e ? 1000 : i;
img.style.opacity = i + 1 === e ? 1 : 0;
} else {
img.style.transform = `translate(${e * -100 + 100}%, 0)`;
if (windowY > elementY) {
carouselScrollToRef.current.scrollIntoView({
behavior: "smooth",
});
}
});
}
};
}

// stop autoplay if user interacts
onRadioChange = e => {
clearInterval(AP_ID);
this.onChange(e);
return nextIndex;
});
};

render() {
const { children, id, nav, fade } = this.props;
const imgArr = this.state.items.map((i, index) => children[index].props);

return (
<div className={`${prefix}--carousel ${id}${fade ? ' fade' : ''}`}>
<div className={`${prefix}--carousel-slide-wrapper`}>
<div className={`${prefix}--carousel-slide ${id}`}>
{imgArr.map((img, i) => (
<img
draggable="false"
src={img.src}
alt={img.alt}
srcSet={img.srcSet}
sizes={img.sizes}
key={`img-${i}`}
className={img.className}
/>
))}
</div>
return (
<section
id={`carousel--${carouselId.current}`}
className={carouselWrapper}
aria-label={carouselLabel}
aria-roledescription="carousel"
{...rest}
>
<span
ref={carouselScrollToRef}
className={scrollAnchor}
aria-hidden="true"
/>
<div className={slidesWrapper}>
<div
id={`carousel--${carouselId.current}-items`}
aria-live="polite"
style={{
transform: `translateX(${
(activeSlideIndex * 100 * -1) / slidesNum
}%)`,
width: slidesNum * 100 + `%`,
}}
>
{children.map((children, index) => {
return (
<div
key={`slide-${index}`}
role="group"
aria-roledescription="slide"
aria-label={`${index + 1} of ${slidesNum + 1}`}
aria-hidden={index !== activeSlideIndex}
style={{
width: 100 / slidesNum + `%`,
flexBasis: 100 / slidesNum + `%`,
}}
>
{children}
</div>
);
})}
</div>
</div>
<div className={slidesNavigation}>
<Button
hasIconOnly
renderIcon={CaretLeft}
iconDescription="Previous slide"
kind="secondary"
aria-controls={`carousel--${carouselId.current}-items`}
disabled={activeSlideIndex === 0}
tooltipAlignment="start"
onClick={() => {
manageActiveSlide("down");
}}
/>
<div className={slidesCounter}>
{activeSlideIndex + 1}/{slidesNum + 1}
</div>
{nav && (
<RadioButtonGroup
className={`${prefix}--carousel-nav-wrapper`}
name={`Carousel navigation ${id}`}
valueSelected={this.state.checkedRadio}
onChange={this.onRadioChange}>
{this.state.items.map(i => (
<RadioButton
className={`${prefix}--carousel-nav-item`}
value={i}
key={i}
labelText=""
/>
))}
</RadioButtonGroup>
)}
<Button
hasIconOnly
renderIcon={CaretRight}
iconDescription="Next slide"
kind="secondary"
aria-controls={`carousel--${carouselId.current}-items`}
disabled={activeSlideIndex === slidesNum}
tooltipAlignment="start"
onClick={() => {
manageActiveSlide("up");
}}
/>
</div>
);
}
}
</section>
);
};

export default Carousel;
42 changes: 42 additions & 0 deletions src/components/Carousel/Carousel.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
.carousel-wrapper {
margin-bottom: $spacing-09;
position: relative;
margin-top: 0;

.slides-wrapper {
overflow: hidden;

> div {
display: flex;
transition: transform;
transform: translateX(0);
transition-timing-function: carbon--motion(standard, productive);
transition-duration: $duration--slow-01;

> div {
flex-grow: 1;
flex-shrink: 0;

> div > div:last-child {
margin-bottom: 0;
}
}
}
}

.slides-navigation {
display: flex;
align-items: center;
margin-top: $spacing-05;

.slides-counter {
margin: 0 $spacing-05;
@include carbon--type-style('body-long-01');
}
}

.scroll-anchor {
position: absolute;
top: -$layout-05;
}
}
Loading
Loading