Skip to content

Commit

Permalink
feat: add feature carousel (#6)
Browse files Browse the repository at this point in the history
* refactor: rename feature section to vision

* refactor: move section shell to new component

* feat: add background color variant and apply to sections

* build(deps): add react-responsive-carousel

* build(deps): add react-icons

* feat: add features carousel

* refactor: rename vision to concepts

* refactor: update features descriptions
  • Loading branch information
kieranroneill authored Nov 15, 2023
1 parent ca6125b commit b7be772
Show file tree
Hide file tree
Showing 48 changed files with 573 additions and 123 deletions.
36 changes: 28 additions & 8 deletions docusaurus.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const darkCodeTheme = require('prism-react-renderer/themes/dracula');
/* eslint-enable @typescript-eslint/no-var-requires */

// directories
const NODE_MODULES_DIR = path.resolve(__dirname, 'node_modules');
const SOURCE_DIR = path.resolve(__dirname, 'src');
const STATIC_DIR = path.resolve(__dirname, 'static');
const STYLES_DIR = path.resolve(SOURCE_DIR, 'styles');
Expand Down Expand Up @@ -48,6 +49,16 @@ const config = {
require.resolve(path.resolve(STYLES_DIR, 'global.scss')),
require.resolve(path.resolve(STYLES_DIR, 'mixins.scss')),
require.resolve(path.resolve(STYLES_DIR, 'navbar.scss')),
// vendor
require.resolve(
path.resolve(
NODE_MODULES_DIR,
'react-responsive-carousel',
'lib',
'styles',
'carousel.min.css'
)
),
],
},
sitemap: {
Expand Down Expand Up @@ -82,20 +93,29 @@ const config = {
items: [
// right
{
label: 'Features',
items: [
{
label: 'Features',
to: '#features',
},
{
label: 'Concepts',
to: '#concepts',
},
],
label: 'Overview',
position: 'right',
to: '#features',
},
{
label: 'Learn',
label: 'Blog',
position: 'right',
sidebarId: 'tutorialSidebar',
type: 'docSidebar',
to: '/blog',
},
{
label: 'Blog',
label: 'Docs',
position: 'right',
to: '/blog',
sidebarId: 'tutorialSidebar',
type: 'docSidebar',
},
],
title: TITLE,
Expand All @@ -107,7 +127,7 @@ const config = {
title: 'Learn More',
items: [
{
label: 'Learn',
label: 'Docs',
to: '/docs/intro',
},
{
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
"husky": "^8.0.3",
"lint-staged": "^13.1.2",
"prettier": "^3.0.3",
"sass": "^1.69.4",
"typescript": "^5.2.2"
},
"dependencies": {
Expand All @@ -70,6 +71,7 @@
"prism-react-renderer": "^1.3.5",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"sass": "^1.69.4"
"react-icons": "^4.12.0",
"react-responsive-carousel": "^3.2.23"
}
}
95 changes: 95 additions & 0 deletions src/components/Carousel/Carousel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import clsx from 'clsx';
import React, { FC } from 'react';
import { PiCaretLeftBold, PiCaretRightBold } from 'react-icons/pi';
import { Carousel as ReactResponsiveCarousel } from 'react-responsive-carousel';

// components
import CarouselButton from './CarouselButton';

// styles
import styles from './index.module.scss';

// types
import { ICarouselItemProps } from './types';

interface IProps {
items: ICarouselItemProps[];
}

const Carousel: FC<IProps> = ({ items }: IProps) => {
return (
<ReactResponsiveCarousel
emulateTouch={true}
renderArrowNext={(
onClickHandler: () => void,
hasNext: boolean,
label: string
) => {
if (hasNext) {
return (
<CarouselButton
Icon={PiCaretRightBold}
isPrev={false}
onClick={onClickHandler}
label={label}
/>
);
}

return null;
}}
renderArrowPrev={(
onClickHandler: () => void,
hasPrev: boolean,
label: string
) => {
if (hasPrev) {
return (
<CarouselButton
Icon={PiCaretLeftBold}
isPrev={true}
onClick={onClickHandler}
label={label}
/>
);
}

return null;
}}
renderIndicator={(onClickHandler, isSelected, index) => {
const key: string = `carousel-indicator-item-${index}`;

if (isSelected) {
return (
<li
aria-label={items[index].label}
className={clsx(styles.indicator, styles['indicator--selected'])}
key={key}
title={items[index].label}
/>
);
}

return (
<li
aria-label={items[index].label}
className={styles.indicator}
onClick={onClickHandler}
onKeyDown={onClickHandler}
key={key}
role="button"
tabIndex={0}
title={items[index].label}
value={index}
/>
);
}}
showStatus={false}
useKeyboardArrows={true}
>
{items.map(({ children }) => children)}
</ReactResponsiveCarousel>
);
};

export default Carousel;
30 changes: 30 additions & 0 deletions src/components/Carousel/CarouselButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import clsx from 'clsx';
import React, { FC } from 'react';
import { IconType } from 'react-icons';

// styles
import styles from './index.module.scss';

interface IProps {
Icon: IconType;
isPrev: boolean;
label: string;
onClick: () => void;
}

const CarouselButton: FC<IProps> = ({ Icon, isPrev, onClick }: IProps) => {
return (
<div
className={clsx(
styles.container,
isPrev ? styles['container--previous'] : styles['container--next']
)}
>
<button className={styles.button} onClick={onClick}>
<Icon className={styles.icon} />
</button>
</div>
);
};

export default CarouselButton;
56 changes: 56 additions & 0 deletions src/components/Carousel/index.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
.button {
background: none;
border: none;
border-radius: 5px;
cursor: pointer;
margin: 0;
padding: 0;
}

.container {
align-items: center;
bottom: 0;
display: flex;
position: absolute;
top: 0;
z-index: 1;

&--next {
right: 0;
}

&--previous {
left: 0;
}
}

.icon {
fill: var(--kb-text-color);
height: 3rem;
transition: color var(--ifm-transition-fast) var(--ifm-transition-timing-default);
width: 3rem;

&:hover {
fill: var(--ifm-color-primary)
}
}

.indicator {
background: var(--ifm-color-primary);
display: inline-block;
filter: alpha(opacity=30);
opacity: 0.3;
//box-shadow: 1px 1px 2px rgba(255, 255, 255, 0.9);
border-radius: 50%;
width: 8px;
height: 8px;
cursor: pointer;
margin: 0 8px;
transition: opacity .25s ease-in;

&--selected,
&:hover {
filter: alpha(opacity=100);
opacity: 1;
}
}
1 change: 1 addition & 0 deletions src/components/Carousel/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './Carousel';
8 changes: 8 additions & 0 deletions src/components/Carousel/types/ICarouselItemProps.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { ReactElement } from 'react';

interface ICarouselItemProps {
children: ReactElement;
label?: string;
}

export default ICarouselItemProps;
1 change: 1 addition & 0 deletions src/components/Carousel/types/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as ICarouselItemProps } from './ICarouselItemProps';
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,15 @@ import Text from '@site/src/components/Text';
import styles from './styles.module.scss';

// types
import { IFeatureItem } from './types';
import { IVisionItemProps } from './types';

type IProps = IFeatureItem;
type IProps = IVisionItemProps;

const Feature: FC<IProps> = ({ title, SvgComponent, description }: IProps) => {
const ConceptItem: FC<IProps> = ({
title,
SvgComponent,
description,
}: IProps) => {
return (
<div className={styles.item}>
<div className={styles['image-container']}>
Expand All @@ -27,4 +31,4 @@ const Feature: FC<IProps> = ({ title, SvgComponent, description }: IProps) => {
);
};

export default Feature;
export default ConceptItem;
Original file line number Diff line number Diff line change
@@ -1,25 +1,28 @@
import React, { FC } from 'react';

// components
import SectionTitle from '../SectionTitle';
import Feature from './Feature';
import Section from '@site/src/components/Section';
import ConceptItem from './ConceptItem';

// images
import feature001Svg from '@site/static/images/feature_001.svg';
import feature002Svg from '@site/static/images/feature_002.svg';
import feature003Svg from '@site/static/images/feature_003.svg';
import concept001Svg from '@site/static/images/concept_001.svg';
import concept002Svg from '@site/static/images/concept_002.svg';
import concept003Svg from '@site/static/images/concept_003.svg';

// styles
import styles from './styles.module.scss';

// types
import { IFeatureItem } from './types';
import { IDefaultSectionProps } from '@site/src/types';
import { IVisionItemProps } from './types';

const FeatureSection: FC = () => {
const features: IFeatureItem[] = [
type IProps = IDefaultSectionProps;

const ConceptsSection: FC<IProps> = ({ variant }: IProps) => {
const items: IVisionItemProps[] = [
{
title: 'Not Just For DeFi',
SvgComponent: feature001Svg,
SvgComponent: concept001Svg,
description: (
<>
Kibisis differs from most wallets by focusing on tokens as a utility,
Expand All @@ -29,7 +32,7 @@ const FeatureSection: FC = () => {
},
{
title: 'AVM Compatible',
SvgComponent: feature002Svg,
SvgComponent: concept002Svg,
description: (
<>
Kibisis not only works with Algorand, but endeavours to encompass all
Expand All @@ -39,7 +42,7 @@ const FeatureSection: FC = () => {
},
{
title: 'Browser Extension',
SvgComponent: feature003Svg,
SvgComponent: concept003Svg,
description: (
<>
Leveraging the security and convenience of browser extensions, Kibisis
Expand All @@ -50,21 +53,19 @@ const FeatureSection: FC = () => {
];

return (
<section className={styles.container}>
<SectionTitle id="#features">Features</SectionTitle>

<Section id="concepts" title="Concepts" variant={variant}>
<div className={styles['items-container']}>
{features.map(({ description, SvgComponent, title }, index) => (
<Feature
{items.map(({ description, SvgComponent, title }, index) => (
<ConceptItem
description={description}
key={`feature-item-${index}`}
key={`vision-item-${index}`}
SvgComponent={SvgComponent}
title={title}
/>
))}
</div>
</section>
</Section>
);
};

export default FeatureSection;
export default ConceptsSection;
3 changes: 3 additions & 0 deletions src/components/ConceptsSection/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { default } from './ConceptsSection';
export { default as ConceptItem } from './ConceptItem';
export * from './types';
Loading

0 comments on commit b7be772

Please sign in to comment.