0.0.19 • Published 3 years ago

@candycode/elements v0.0.19

Weekly downloads
30
License
MIT
Repository
github
Last release
3 years ago

 

 

Version Downloads Discord

 

 

Overview

The @candycode/elements library consists of atomic components used to assemble custom React components with animations and interactivity powered by react-spring and react-use-gesture respectively.

See this CodeSandbox example for a quick demonstration of how several different custom <Carousel> components—each having their own unique appearance and behavior—can be assembled using the same set of atomic components from this library.

Note that peer dependencies are not bundled with this library and must be installed separately. This prevents having multiple versions of the same dependency installed for projects that already have react-spring and related packages installed for other purposes.

Feel free to join us on Discord for assistance and please star this project on GitHub if you enjoy using it. Thanks! 🥰

Installation

yarn

yarn add @candycode/elements react-spring react-use-gesture react-use-measure jotai

npm

npm install @candycode/elements react-spring react-use-gesture react-use-measure jotai

After installing the package and required peer dependencies, follow instructions for one or more of the below component APIs to assemble your own custom, interactive React components, each with their own unique appearance and behavior. Some components require basic styles to work so be careful to check if there is a "supporting styles" section for the chosen component.

Carousel

These atomic components can be used to assemble a custom <Carousel> component. Every rendered component accepts className and style props (as well as all other props accepted by a React DOM node). These props may be used to supplement the basic styles provided in the styles.css file (and the "supporting styles" section below), which must be manually added to your project and contain only the minimal CSS necessary for the carousel to function.

Carousel state

Provider

A <Provider> component must be the parent of all other carousel components and accepts the following props.

propdefaulttypedetails
totalSlides(required)numberthe value must match the length of the children prop passed to the <Track> component
aspectRatioundefinedundefined, 'square', 'wide', 'wider', 'widest', 'tall', 'taller', or 'tallest'enables and sets a fixed aspect ratio for the carousel
orientation'horizontal''horizontal' or 'vertical'determines the orientation of the carousel track (experimental)
springConfig'default''default', 'gentle', 'wobbly', 'stiff', 'slow', 'molasses', or { mass: number, tension: number, friction: number }determines the settings used to power the carousel animations
focusMode'auto''auto', 'manual', or 'always''auto' toggles keyboard, mouse, and touch interactivity based on whether or not the carousel is visible in the viewport'manual' enables interactivity when the carousel is hovered, clicked, or touched and disables it when something outside the carousel is clicked or touched'always' always enables interactivity; best when used for carousels that are permanently visible within in the viewport
inViewThreshold0.1numberthe number between 0 and 1 indicating the percentage of the carousel that must be visible in the viewport before automatically gaining focus when focusMode is set to 'auto'
allowGesturestruebooleanenable/disable mouse and touch support
dragThreshold50numberthe number of pixels the track must be panned to initiate an automatic slide change
allowKeyboardtruebooleanenable/disable keyboard support
keyboardMode'standard''standard' or 'gaming''standard' listens to , , , keyboard codes for moving through slides'gaming' listens to W, A, S, D in addition to all 'standard' keys
allowExpansiontruebooleanenable/disable expanded-mode support
allowFullscreentruebooleanenable/disable fullscreen-mode support

Carousel elements

Wrapper

A <Wrapper> component must be placed somewhere within the <Provider> component. It must contain a <Track> component. It may also include a <Drawer> component and other React nodes.

Track

A <Track> component must be placed somewhere within the <Wrapper> component. It must contain one or more <Slide> components as direct children.

Slide

Each <Slide> component must be placed directly under the <Track> component. Each <Slide> can only accept one direct child node, which will be stretched to cover the entire slide.

Drawer

A <Drawer> component may be placed somewhere within the <Wrapper> component, but outside the <Track> component. It can be used to contain one or more control elements or other React nodes.

Controls

The following control components include built-in interactivity on click and touch events. They may be placed anywhere within the <Provider> component and can accept a children prop to wrap its logic and behavior around any React node.

  • <Start> moves to the first slide
  • <Backward> moves to the previous slide
  • <Forward> moves to the next slide
  • <End> moves to the last slide
  • <Expand> toggles expanded mode
  • <Fullscreen> toggles fullscreen mode
import React from 'react';
import {
  Provider,
  Wrapper,
  Track,
  Slide,
  Drawer,
  Start,
  Backward,
  Forward,
  End,
  Expand,
  Fullscreen,
} from '@candycode/elements/carousel';

export const Carousel = ({ children, ...rest }) => {
  return (
    <Provider totalSlides={children.length ? children.length : 1} {...rest}>
      <Wrapper>
        <Track>
          {children.length ? (
            children.map((child, index) => <Slide key={index}>{child}</Slide>)
          ) : (
            <Slide>{children}</Slide>
          )}
        </Track>
        <Drawer>
          <Start />
          <Backward />
          <Expand />
          <Fullscreen />
          <Forward />
          <End />
        </Drawer>
      </Wrapper>
    </Provider>
  );
};
import React from 'react';
import {
  Provider,
  Wrapper,
  Track,
  Slide,
  Drawer,
  Backward,
  Forward,
  Expand,
  Fullscreen,
} from '@candycode/elements/carousel';

import { BackIcon, NextIcon, ExpandIcon, FullscreenIcon } from './icons';

export const Carousel = ({ children, ...rest }) => {
  return (
    <Provider totalSlides={children.length ? children.length : 1} {...rest}>
      <Wrapper>
        <Track>
          {children.length ? (
            children.map((child, index) => (
              <Slide key={index} className="pb-16">
                {child}
              </Slide>
            ))
          ) : (
            <Slide className="pb-16">{children}</Slide>
          )}
        </Track>
        <Backward className="absolute left-0 p-4">
          <BackIcon />
        </Backward>
        <Forward className="absolute right-0 p-4">
          <NextIcon />
        </Forward>
        <Drawer className="h-16 bg-black bg-opacity-50 text-white">
          <Expand className="p-4">
            <ExpandIcon />
          </Expand>
          <Fullscreen className="p-4">
            <FullscreenIcon />
          </Fullscreen>
        </Drawer>
      </Wrapper>
    </Provider>
  );
};
import React from 'react';
import { Carousel } from './carousel';

export const Pokemon = () => {
  return (
    <Carousel>
      <div>Bulbasaur</div>
      <div>Charmander</div>
      <div>Squirtle</div>
      <div>Pikachu</div>
      <div>Eevee</div>
      <div>Togepi</div>
    </Carousel>
  );
};

Use of the assembled <Carousel> component requires the following styles.

Basic carousel functionality (required)

.carousel {
  position: relative;
  overflow: hidden;
}

.carousel > div {
  display: flex;
  align-items: center;
}

.carousel-track {
  position: relative;
  z-index: 0;
  display: flex;
  height: 100%;
}

.carousel-slide {
  min-width: 100%;
  width: 100%;
  max-width: 100%;
  min-height: 100%;
  height: 100%;
  max-height: 100%;
}

.carousel-slide > * {
  min-width: 100% !important;
  width: 100% !important;
  max-width: 100% !important;
  min-height: 100% !important;
  height: 100% !important;
  max-height: 100% !important;
}

.carousel-slide img {
  pointer-events: none !important;
  user-select: none !important;
}

.carousel-button--disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

Aspect ratio functionality (optional)

[class*='carousel--aspect-ratio-'] {
  height: 0;
}

[class*='carousel--aspect-ratio-'] > div {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  bottom: 0;
}

.carousel--aspect-ratio-square {
  padding-top: 100%;
}

.carousel--aspect-ratio-wide {
  padding-top: 75%;
}

.carousel--aspect-ratio-wider {
  padding-top: 56.25%;
}

.carousel--aspect-ratio-widest {
  padding-top: 42.1875%;
}

.carousel--aspect-ratio-tall {
  padding-top: 133.333333%;
}

.carousel--aspect-ratio-taller {
  padding-top: 177.777778%;
}

.carousel--aspect-ratio-tallest {
  padding-top: 233.333333%;
}

Gesture functionality (optional)

.carousel--gestures {
  touch-action: none;
}

.carousel--gestures * {
  user-select: none;
}

Expansion functionality (optional)

.carousel--expanded > div {
  position: fixed !important;
  left: 0;
  right: 0;
  top: 0;
  bottom: 0;
  z-index: 1000;
  background: rgba(0, 0, 0, 0.5);
}

<Drawer> component (optional)

.carousel-drawer {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 10;
  display: flex;
  justify-content: space-around;
  align-items: center;
}

Disclosure

These atomic components can be used to assemble a custom <Disclosure> component. Every component accepts className and style props (as well as all other props accepted by a React DOM node) to facilitate custom designs.

Disclosure components

The <Provider> component is the main export and accepts the following props.

propdefaulttypedetails
uid(required)stringunique identifier linking the disclosure panel and button
springConfig'default''default', 'gentle', 'wobbly', 'stiff', 'slow', 'molasses', or { mass: number, tension: number, friction: number }determines the settings used to power the disclosure animation

The <Button> subcomponent must be exported as Disclosure.Button. It toggles the visibility of the <Panel> component.

The <Panel> subcomponent must be exported as Disclosure.Panel. Its visibility is toggled by the <Button> component.

import React from 'react';
import { Provider, Button, Panel } from '@candycode/elements/disclosure';

export const Disclosure = ({ children, ...rest }) => {
  return <Provider {...rest}>{children}</Provider>;
};

const CustomButton = ({ children, ...rest }) => {
  return <Button {...rest}>{children}</Button>;
};

const CustomPanel = ({ children, ...rest }) => {
  return <Panel {...rest}>{children}</Panel>;
};

Disclosure.Button = CustomButton;
Disclosure.Panel = CustomPanel;
import React from 'react';
import { Disclosure } from './disclosure';

export const FAQ = () => {
  return (
    <>
      <Disclosure uid="faq_1">
        <Disclosure.Button>Who’s that Pokémon?</Disclosure.Button>
        <Disclosure.Panel>It’s Pikachu!</Disclosure.Panel>
      </Disclosure>
      <Disclosure uid="faq_2">
        <Disclosure.Button>What’s Pikachu's Pokédex ID?</Disclosure.Button>
        <Disclosure.Panel>Twenty five.</Disclosure.Panel>
      </Disclosure>
      <Disclosure uid="faq_3">
        <Disclosure.Button>Where’s Pikachu found?</Disclosure.Button>
        <Disclosure.Panel>Viridian Forest.</Disclosure.Panel>
      </Disclosure>
    </>
  );
};

Contributing and roadmap

The library is currently in very active development. Existing components are receiving additional functionality. New components will include:

  • <Accordion> (in development)
  • <Modal>
  • <Reveal>
  • <Slide>
  • <Toggle> (in development)
  • <Wobble>

We are also seeking additional contributors. Feel free to join us on Discord to discuss opportunities for further development.

0.0.19

3 years ago

0.0.18

3 years ago

0.0.17

3 years ago

0.0.15

3 years ago

0.0.16

3 years ago

0.0.14

3 years ago

0.0.13

3 years ago

0.0.12

3 years ago

0.0.11

3 years ago

0.0.10

3 years ago

0.0.9

3 years ago

0.0.8

3 years ago

0.0.7

3 years ago

0.0.6

3 years ago

0.0.5

3 years ago

0.0.4

3 years ago

0.0.3

3 years ago

0.0.2

3 years ago

0.0.1

3 years ago