Skip to content

Introducing CSS cascading to React components with first-class Typescript support right out of the box.

License

Notifications You must be signed in to change notification settings

codyduong/react-cascade-component

Repository files navigation

downloads per/month gzip size module formats: umd, cjs, esm Code Coverage

React-Cascade-Component

Introducing CSS cascading to React components with first-class Typescript support right out of the box.

DRY out your code. Repeat yourself less with less hassle.

Installation

npm yarn
npm install react-cascade-component yarn add react-cascade-component

Example Use Case

import Cascade from 'react-cascade-component';

const App = () => {
  const onClickHandler: React.MouseEventHandler<HTMLButtonElement> = (e) => {
    alert(`Button ${e.currentTarget.id} was clicked!`);
  };

  return (
    <Cascade cascadeTo="button" cascadeProps={{ onClick: onClickHandler }}>
      <div />
      <div />
      <button id="1" />
      <button id="2" />
      <button id="3" />
    </Cascade>
  );
};

Cascade by default is a div element but can be of any JSX.IntrinsicElement by setting the as prop value or using Cascade.[JSX.IntrinsicElement] .

<Cascade as="span">{/* ... */}</Cascade>
<Cascade.span>{/* ... */}</Cascade.span>

API

prop type examples

as

Specifies what <Cascade/> is rendered as. Defaults to div
undefined

keyof JSX.IntrinsicElement

React.JSXElementConstructor<any>
"div"

"span"

MyCustomComponent

cascadeTo

Specifies which child elements cascadeProps is sent to. If null will send cascadeProps to all children elements. If undefined will default to the outer cascadeTo if inside another <Cascade> component. If not, it will send to all children elements.
undefined

null

keyof JSX.IntrinsicElement

React.JSXElementConstructor<any> (keyof JSX.IntrinsicElement | React.JSXElementConstructor<any>)[]

"div"

["span", "div"]

MyCustomComponent

['span', MyCustomComponent]

cascadeProps

The `props` to cascade to child elements
any1 {"className": "foobar", "customKey": "customValue"}

absorbProps

Whether or not the <Cascade> will absorb the properties itself, or simply pass it on. Defaults to true
boolean true false

...rest

Any other properties to be used by <Cascade>
JSX.IntrinsicElement[typeof as] Valid other properties may be ref className id and more

1 - The is not technically true, but practically true. It has a type dependent on cascadeTo, and will have type inference if cascadeTo is provided

Demos

CascadeTo Callback

The <Cascade> component has a callback parameter on cascadeTo which means you can specify handling, by default the callback is (callbackProps, originalProps) => {...callbackProps, ...originalProps}, ie. a shallow merge.

<Cascade
  cascadeTo={[
    ['button', (c, o) => ({ ...c.buttonProps, ...o })],
    [MyCustomComponent, (c, o) => ({ ...c.customProps, ...o })],
  ]}
  cascadeProps={{
    buttonProps: {
      onClick: onClickHandler,
    },
    customProps: {
      className: 'foobar'
    }
  }}
>
  <button />
  <div />
  <MyCustomComponent />
</Cascade>

You can also specify a function instead:

<Cascade
  cascadeTo={(t, c, o) => {
    if (t === 'button') {
      return {...c.buttonProps, ...o}
    }
    if (t === MyCustomComponent) {
      return {...c.customProps, ...o}
    }
  }}
  cascadeProps={{
    buttonProps: {
      onClick: onClickHandler,
    },
    customProps: {
      className: 'foobar'
    }
  }}
>
  <button />
  <div />
  <MyCustomComponent />
</Cascade>

Nested Cascades

The <Cascade> component can pass through to each other. By default it will both absorb and pass properties. Nested <Cascade> components will cascadeTo the same constrained types. <Cascade absorbProps={false} /> will disable absorbing props but will still pass through through properties.

<Cascade className="foo" cascadeProps={{ className: 'bar' }}>
  <Cascade 
    className="bang" 
    cascadeTo={[Cascade, 'span']}
  >
    <Cascade.span>                {/* Cascade.span !== 'span' */}         
      <div />
      <div />
      <span />                    
      <label />
    </Cascade.span>
    <Cascade 
      cascadeTo={null}
    >                     
      <span />                    {/* className="bar" */}
      <label />                   {/* className="bar" */}
    </Cascade>
    <Cascade>                     {/* className="bar" */}
      <span />                    {/* className="bar" */}
      <label />                   
    </Cascade>
    <Cascade                      /* className=undefined */
      absorbProps={false}
      cascadeTo="label"
    > 
      <span />                    
      <label />                   {/* className="bar" */}
    </Cascade>
    <div>
      <span />
      <label />
    </div>
  </Cascade>
</Cascade>;

Alternatives

If you are simply using react-cascade-component as a means to transfer props deeply in your component, instead consider using React's built-in useContext .

Contribute

Contributions are welcome!

License

Licensed under the MIT License, Copyright © 2023-present Cody Duong.

See LICENSE for more information.

About

Introducing CSS cascading to React components with first-class Typescript support right out of the box.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published