npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

njmyers-component-library

v0.1.24

Published

React component library

Downloads

4

Readme

Component Library

This is a collection of useful and maybe useless React components. In general components will follow a few rules in order to increase usability. These rules in paricular are applied in how the components will use inline styles

Global Usage notes

Inline Styling

  1. All components accept className prop.
  2. For a shallow merge of styles, use the style prop on a component.
  3. To escape all inline styles, pass an empty object to replaceStyle prop.
  4. For a custom reusable component, redefine the defaultProps with a new replaceStyle object. Don't forget to merge all of the other defaultProps!
<Component className="class" />
// behaves as expected - inline styles take precendence over rules applied to className
<Component style={{ color: 'red' }} />
// shallowly merges style with the default inline styles of the component
<Component replaceStyle={{}} />
// all inline styles are removed

Animation Props

Status Switch

Wrap your React Components in a StatusSwitch to conditionally render based on status props.

import React from 'react';

class Stateful extends React.Component {
  state = {
    status: 'initial',
  };

  toLoading = () => this.setState({ status: 'loading' });

  toError = () => this.setState({ status: 'error' });

  render() {
    return (
      <StatusSwitch status={this.state.status}>
        <div>
          <p>Some content</p>
        </div>
      </StatusSwitch>
    );
  }
}

Also you can pass in your own components as render props loading or error so you can customize the error and loading renders

class Stateful extends React.Component {
  render() {
    return (
      <StatusSwitch
        status={this.state.status}
        error={(props) => <p>error</p>}
        loading={(props) => <p>loading</p>}
      >
        <div>
          <p>Some content</p>
        </div>
      </StatusSwitch>
    );
  }
}

Form Components

A selection of React form components. Meant to assist in adding labels and making accessibility automatic. Also helps in generating css classes and modifiers.

Input

import React from 'react';

class Form extends Component {
  state = {
    age: 0
  }

  this.onChange(event) => {
    this.setState({
      [event.target.name]: event.target.value
    })
  }

  render() {
    <form>
      <Input
        value={this.state.value}
        onChange={this.onChange}
        className="formInput"
        name="age"
        label="Your Age"
      />
    </form>;
  }
}

SemiSticky

SemiSticky is a position aware component that animates in and out on scroll positions. It is inspired by the CSS property position: sticky but allows for usage in a much wider variety of situations. SemiSticky uses the AnimationProps for determining it's style. Please see AnimationProps for more information about usage.

SemiSticky uses a single prop to determine the scroll position of it's on state. That prop is called top and it signifies the amount of pixels from the top of the page that the component should apply it's onState styles. Anything greater then top will apply the onState styles and anything less then top will apply the offState styles.

Usage

A common usage pattern is for fixed position headers and footers that will show themselves based on a user's scroll position.

import React from 'react';

class Main extends React.Component {
  render() {
    return (
      <SemiSticky top={400}>
        <Header />
      </SemiSticky>
    );
  }
}

In the above example, the Header component will hide from view when the user scrolls down 400 pixels. Sounds simple enough but to implement yourself takes many lines of code! Feel free to add your own onState and offState styles and SemiSticky will automatically generate CSS transitions.

If you would like to shallowly merge styles, use the style prop. If you want to replace the default inline styles, use the replaceStyle prop. You can also apply className prop but keep in mind that all of the inline styles will take precedence.

Props

| Prop Name | Type | Required | Default Value | Description | |:----------|:-----|:---------|:--------------|:------------| |children|ReactNode|false|-|react children (your component)| |className|string|false|-|className applied to the container element| |offState|{}|false|{ transform: 'translateY(-100px)',}|css inline styles applied to the off state| |onState|{}|false|{ transform: 'translateY(0)',}|css inline styles applied to the on state| |replaceStyle|{}|false|{ position: 'fixed', background: 'rgba(0, 0, 0, 0.8)', width: '100%', top: 0, left: 0,}|completely replace all styles| |sizes|{ isSemiStickyActive: boolean,}|true|-|inherited sizing info| |style|{}|false|-|shallowly merge styles| |top|number|false|200|distance from the document top to engage the on state styles| |transitionSpeed|number|false|0.25|the speed of the transition| |transitionTiming|| 'ease'| 'linear'| 'ease-in'| 'ease-out'| 'ease-in-out'| 'step-start'| 'step-end'|false|'ease'|the transition timing function|

LazyImage

A component for lazily loading images

Usage

<LazyImage src={src} placeholder={placeholder} />

Props

| Prop Name | Type | Required | Default Value | Description | |:----------|:-----|:---------|:--------------|:------------| |alt|string|false|-|pass-thru alt tag for image| |baseStyles|{}|false|{ margin: 0, padding: 0, width: '100%', height: 'auto', backfaceVisibility: 'inherit',}|base styles applied to all elements| |className|string|true|-|class name applied to the components in BEM style| |containerStyle|{}|false|{ position: 'relative', overflow: 'hidden', // fix for image element whitespace lineHeight: 0,}|shallow merge of styles applied to the container| |imageStyle|{}|false|{}|shallow merge of styles applied to the highres| |name|string|false|-|name attribute for onclick events| |onClick|Function|false|-|pass-thru onclick function| |placeholder|string|true|-|placeholder image src| |placeholderStyle|{}|false|{ position: 'absolute', top: 0, left: 0, bottom: 0, right: 0, objectFit: 'contain', transition: '0.25s opacity',}|shallow merge of styles applied to the placeholder| |src|string|true|-|high-res image src| |title|string|false|-|pass-thru title tag for image|

Modal

Modal creates ... a modal. It uses ReactDOM.createPortal so it renders your element outside of the HTML tree. However it is still controlled by whichever react parent component it is used in. By default Modal renders to the id 'modal-root'. Please be sure to add 'modal-root' to your HTML file or else nothing will be rendered by this component.

The default export from Modal is the styled modal. It contains background, transitions and all sorts of fun inline css. If you would like to use only the portal creation you can find it as a named export UnstyledModal.

import { Modal, UnstyledModal } from 'njmyers-component-library';

Usage

Simply add your children and use the parent component to control the status as 'on' or 'off'.

import React from 'react';
import { Modal } from 'njmyers-component-library';

class ModalParent extends React.Component {
  state = {
    status: 'on'
  }

  turnOffModal = () => {
    this.setState({
      status: 'off'
    })
  }

  componentDidMount() {
    setTimeout(this.turnOffModal, 4000)
  }

  render() {
    return (
      <Modal status={this.state.status} />
        <p>Annoying advertisement</p>
      </Modal>
    )
  }
}

In the above example, the 'annoying advertisement' will render itself in aside under the 'modal-root' id for 4 seconds and then will animate to its offState styles. When ModalParent unmounts the modal component will be removed from 'modal-root'.

Props

| Prop Name | Type | Required | Default Value | Description | |:----------|:-----|:---------|:--------------|:------------| |children|ReactNode|false|-|react children (your component)| |className|string|false|-|className applied to the container element| |modalRoot|string|false|'modal-root'|div id where to render the react portal| |offState|{}|false|{ opacity: 0,}|css inline styles applied to the off state| |onState|{}|false|{ opacity: 1,}|css inline styles applied to the on state| |replaceStyle|{}|false|{ background: 'rgba(255, 255, 255, 0.8)', position: 'fixed', width: '100%', height: '100%', top: 0, left: 0, bottom: 0, right: 0, display: 'flex', justifyContent: 'center', alignItems: 'center',}|completely replace all styles| |status|'on' | 'off'|false|'off'|display status of the styled modal| |style|{}|false|{}|shallowly merge styles| |transitionSpeed|number|false|0.25|the speed of the transition| |transitionTiming|| 'ease'| 'linear'| 'ease-in'| 'ease-out'| 'ease-in-out'| 'step-start'| 'step-end'|false|'ease'|the transition timing function| |zIndexOff|number|false|-20|z-index of the modal when off| |zIndexOn|number|false|1|z-index of the modal when on|

BEM

Background (Frustration)

I've read many articles about using BEM syntax. As I understand it the main points are as follows.

  1. BEM syntax reduces css specificity (GOOD)
  2. BEM syntax can be implemented easily with preprocessors (GOOD)
  3. BEM syntax makes your HTML or Components look ugly (BAD!)

If you stick to BEM syntax then you end up with a React Component like this

<p className="block-element block_element-modifier block_element-otherModifier">
  Text
</p>

And sass or scss that looks like this

.block
  &_element
    &-modifier
    &-otherModifier

The sass looks like great IMHO but the markup looks terrible. How you want to write your sass or scss is a topic for another conversation. All of the aforementioned articles then proceed to explain how they have made adjustments on the BEM syntax so that it is easier to read. Usually this means creating shorter classNames and using tricks.

TODO ADD EXAMPLE

Now of course this is a good thing as we want our source code to be cleaner and easier to read. However it comes at the cost of possible reducing the specificity of your compilied css code. Why do we have to make a sacrifice?

Solution

Now we have a brand new component for writing BEM classNames to your components. We hopefully get the best of both worlds by being able to using the full power of BEM in terms of specificity while using a much cleaner syntax in React. Behold the power of <BEM>

class Menu extends React.Component {
  static defaultProps = {
    menuItems: [
      {
        text: 'Link1',
        id: 1,
      },
      {
        text: 'Link2',
        id: 2,
      },
      {
        text: 'Link3',
        id: 3,
      },
    ],
  };

  state = {
    active: 1,
  };

  render() {
    return (
      <BEM block="menu">
        <nav>
          {this.props.menuItems.map((item) => (
            <div element="buttonContainer">
              <button
                element="button"
                modifiers={this.state.active === item.id ? 'active' : ''}
              >
                {item.text}
              </button>
            </div>
          ))}
        </nav>
      </BEM>
    );
  }
}

Now what will this render? Something like this of course depending on which data you pass in

<nav class="menu">
  <div element="buttonContainer" class="menu_buttonContainer">
    <button element="button" modifiers="active" class="menu_button menu_button-active">Link1</button>
  </div
  ><div element="buttonContainer" class="menu_buttonContainer">
    <button element="button" modifiers="" class="menu_button">Link2</button>
  </div>
  <div element="buttonContainer" class="menu_buttonContainer">
    <button element="button" modifiers="" class="menu_button">Link3</button>
  </div>
</nav>

Great... not only do we have the wordy classes all properly applied we also have searchable property strings associated with each element. Yay success

How would it look manually applying classNames to this component?

class Menu extends React.Component {
  // ... data here
  render() {
    return (
      <nav className="menu">
        {this.props.menuItems.map((item) => (
          <div className="menu_buttonContainer">
            <button
              className={`menu_button ${
                this.state.active === item.id ? 'menu_Button-active' : ''
              }`}
            >
              {item.text}
            </button>
          </div>
        ))}
      </nav>
    );
  }
}

Not too bad. But what if we want to apply modifiers then it gets messy really quickly

class Menu extends React.Component {
  // ... data here
  render() {
    return (
      <nav className="menu menu-right">
        {this.props.menuItems.map((item) => (
          <div className="menu_buttonContainer menu_buttonContainer-square">
            <button
              className={`menu_button ${
                this.state.active === item.id ? 'menu_button-active' : ''
              } ${this.state.animate === item.id ? 'menu_button-animate' : ''}`}
            >
              {item.text}
            </button>
          </div>
        ))}
      </nav>
    );
  }
}

Ughhh gross. You might try to clean it up with a helper function.

class Menu extends React.Component {
  getButtonClass = (id) => {
    return `menu_button ${
      this.state.active === id ? 'menu_button-active' : ''
    } ${this.state.animate === id ? 'menu_button-animate' : ''}`;
  };
  // ... data here
  render() {
    return (
      <nav className="menu menu-right">
        {this.props.menuItems.map((item) => (
          <div className="menu_buttonContainer menu_buttonContainer-square">
            <button className={this.getButtonClass(item.id)}>
              {item.text}
            </button>
          </div>
        ))}
      </nav>
    );
  }
}

Well why not just use <BEM> ?

class Menu extends React.Component {
  // same data from before

  state = {
    active: 1,
    animate: 1,
  };

  getModifiers = (id) => {
    return Object.entries(this.state)
      .filter((entry) => entry[1] === id)
      .map((entry) => entry[0]);
  };

  render() {
    return (
      <BEM block="menu">
        <nav modifier="right">
          {this.props.menuItems.map((item) => (
            <div element="buttonContainer" modifiers="square">
              <button element="button" modifiers={this.getModifiers(item.id)}>
                {item.text}
              </button>
            </div>
          ))}
        </nav>
      </BEM>
    );
  }
}

And that will produce the following html

<nav modifier="right" class="menu">
    <div element="buttonContainer" modifiers="square" class="menu_buttonContainer menu_buttonContainer-square">
        <button element="button" modifiers="active,animate" class="menu_button menu_button-active menu_button-animate">Link1</button>
    </div>
    <div element="buttonContainer" modifiers="square" class="menu_buttonContainer menu_buttonContainer-square">
        <button element="button" modifiers="" class="menu_button">Link2</button>
    </div>
    <div element="buttonContainer" modifiers="square" class="menu_buttonContainer menu_buttonContainer-square">
        <button element="button" modifiers="" class="menu_button">Link3</button>
    </div>
</nav>

And now you can write your sass the same way

.menu
  &_buttonContainer
    &-square
  &_button
    &-animate
    &-active

Anyways you may think differently but this syntax makes alot of sense for me!

Notes

BEM will only add classes to dom elements and not to Components. In the below example the p entities will receive classNames "block_element" and "block_otherElement" repectively. No classNames will be added to the <SpecialComponent />. This is a design feature not an error. We would assume that you will use another instance of BEM inside your special component as it is special enough to warrant it's own component! This frees you up for using the create rich environment of composing components together.

render() {
  return (
    <BEM block="block">
      <p element="element">text</p>
      <p element="otherElement">text</p>
      <SpecialComponent />
    </BEM>
  )
}

All classNames are added to previously existing classNames. This means you can still use things like font awesome and existing classNames as BEM builds on top of already existing features. The i element below would have the className of "fa block_element"

render() {
  return (
    <BEM block="block">
      <i className="fa" element="element">text</i>
    </BEM>
  )
}