Giter Site home page Giter Site logo

camwest / react-slot-fill Goto Github PK

View Code? Open in Web Editor NEW
655.0 16.0 27.0 3.24 MB

Slot & Fill component for merging React subtrees together. Portal on steroids.

Home Page: https://camwest.github.io/react-slot-fill/

License: Other

HTML 2.32% CSS 4.23% JavaScript 14.25% TypeScript 79.20%
react javascript framework extensibility

react-slot-fill's Introduction

react-slot-fill · CircleCI Status Codacy Badge PRs Welcome

Image

Slot & Fill component for merging React subtrees together.

Installation

npm install react-slot-fill --save

Check out the examples locally

git clone https://github.com/camwest/react-slot-fill
cd react-slot-fill
npm start

Usage

Note These examples use React Fiber Alpha

Toolbar.js

import { Slot, Fill } from 'react-slot-fill';

const Toolbar = (props) =>
  <div>
    <Slot name="Toolbar.Item" />
  </div>

export default Toolbar;

Toolbar.Item = (props) =>
  <Fill name="Toolbar.Item">
    <button>{ props.label }</button>
  </Fill>

Feature.js

import Toolbar from './Toolbar';

const Feature = () =>
  [
    <Toolbar.Item label="My Feature!" />
  ];

App.js

import Toolbar from './Toolbar';
import Feature from './Feature';

import { Provider } from 'react-slot-fill';

const App = () =>
  <Provider>
    <Toolbar />
    <Feature />
  </Provider>

ReactDOMFiber.render(
  <App />,
  document.getElementById('root')
);

Components

Creates a Slot/Fill context. All Slot/Fill components must be descendants of Provider. You may only pass a single descendant to Provider.

interface Provider {
  /**
   * Returns instances of Fill react components
   */
  getFillsByName(name: string): Fill[];
  /**
   * Return React elements that were inside Fills
   */
  getChildrenByName(name: string): React.ReactChild[];
}

getFillsByName and getChildrenByName are really useful for testing Fill components. See src/lib/__tests/Provider.test.tsx for an example.

Expose a global extension point

import { Slot } from 'react-slot-fill';

Props

interface Props {
  /**
   * The name of the component. Use a symbol if you want to be 100% sue the Slot
   * will only be filled by a component you create
   */
  name: string | Symbol;

  /**
   * Props to be applied to the child Element of every fill which has the same name.
   *
   *  If the value is a function, it must have the following signature:
   *    (target: Fill, fills: Fill[]) => void;
   *
   *  This allows you to access props on the fill which invoked the function
   *  by using target.props.something()
   */
  fillChildProps?: {[key: string]: any}

  /**
   * A an optional function which gets all of the current fills for this slot
   * Allows sorting, or filtering before rendering. An example use-case could
   * be to only show a limited amount of fills.
   *
   * By default Slot injects an unstyled `<div>` element. If you want greater
   * control over presentation use this function.
   *
   * @example
   * <Slot name="My.Slot">
   * {(items) => <Component>{items}</Component>}
   * </Slot>
   */
  children?: (fills) => JSX.Element
}

Render children into a Slot

import { Fill } from 'react-slot-fill';

Props

interface Props {
  /**
   * The name of the slot that this fill should be related to.
   */
  name: string | Symbol

  /**
   * one or more JSX.Elements which will be rendered
   */
  children: JSX.Element | JSX.Element[]
}

You can add additional props to the Fill which can be accessed in the parent node of the slot via fillChildProps.

react-slot-fill's People

Contributors

brandonboone avatar camwest avatar craga89 avatar matthieulemoine avatar nathan-smith avatar otbe avatar rogeliog avatar rozzzly avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

react-slot-fill's Issues

NPM package.json contains an invalid peer dependency

Looking at the package.json in this repo, it contains the following peer dependency

"prop-types": "^15.5.8",

However, when using the 2.0.1 package from NPM I get this in the package.json

"prop-types": "^16.0.0",

I also see the same when I view the package.json through unpkg (in case I had some weird build step ruining all of my packages) here.

This doesn't have any run time impact as far as I can tell but does put a couple warnings into my console about having invalid peer dependencies. Would be nice to clean these up and I was going to submit a PR to fix it but it appears to only be an issue in the published package.

Current npm builds contain "const"

Hi,

Im struggling with some older IE versions (IE10) and react-slot-fill. It seems to me that both builds distributed via npm contains a const declaration:

const __assign = Object.assign || function (target) {
    for (var source, i = 1; i < arguments.length; i++) {
        source = arguments[i];
        for (var prop in source) {
            if (Object.prototype.hasOwnProperty.call(source, prop)) {
                target[prop] = source[prop];
            }
        }
    }
    return target;
};

which will fail with syntax errors in older browsers. I guess this code snippet is TypeScript related.

Any advice how to fix this?

Thanks!

TypeError: Cannot read property 'onComponentsChange' of undefined

Hi, thanks for your awsome work. i try to create a some toolbar with this library , and i want when route is changed and some component mounted the toolbar is changed . so this is my toolbar code :

import {Fill, Slot} from 'react-slot-fill';

class UniversalToolbar2 extends Component{
  render(){
    return(
      <div>
        <Slot name="UniversalToolbar2.Item"/>
      </div>
    )
  }
}

UniversalToolbar2.Item = (props)=> <Fill name="UniversalToolbar2.Item"><span>Hope</span></Fill>

and im my App.js page on top of routes i isner provider , one problem that happend is it confilic with redux-thunk provider so i imported it's like this :
import {Provider as slotAndFill} from "react-slot-fill";

and this is App.js

class App extends React.Component {
  render() {
    return (
      <div>
        <slotAndFill>
          <UniversalToolbar2 />
          <UniversalBreadCrumb {...this.props} />
          <UniversalToolbar {...this.props} />
          <Layout>
            <Content>
              <Switch>
                <Route
                  path="/"
                  render={props => <AppList {...props} {...this.props} />}
                />
              </Switch>
            </Content>
          </Layout>
        </slotAndFill>
      </div>
    );
  }
}
export default connect(mapStateToProps, mapDispachToProps)(App);

and then in one of my component i try to fill the slot so :

class AppList extends Component {
  componentDidMount() {
    this.props.getAllAppsByUserId(""); //test for getting all Apps
    this.props.setUserNavigationAttributesEnableApplications();
  }
  render() {
    return (
      <div>
        <AppToolbar2/>
        <Switch>
        <Route
          path="/app/:appId/versions"
          render={props => <VersionsPage {...this.props} {...props} />}
        />
        <Route
          path="/"
          render={props => <MainPage {...this.props} {...props} />}
        />
      </Switch>
      </div>
    );
  }
}

and this is AppToolbar2 that:

import React, { Component } from "react";
import UniversalToolbar2 from "../../UniversalComponents/UniversalToolbar/UniversalToolbar2";
class AppToolbar2 extends Component {
  render() {
    return (
        <UniversalToolbar2.Item/>
    );
  }
}
export default AppToolbar2;

but i get this error :
fillandslot1

Maintenance

Hi there!

This repository is currently outdated. My suggestion is to split the demo package from the lib itself so they can be updated at a different pace.

Also, now there's the opportunity to play with the new Hooks API, I would like this library to go forward.

As part of the Contributing guide, I'm an Intent to implement.

Compare Slot/Fill to React 16 Portals?

Can you provide a brief description of pro/cons for using this repo vs using Portals in React 16?

Very cool concepts here btw, dynamic component extensibility would certainly be useful imo.

Thanks.

1.0.0 Roadmap

Themes

  • replace the prototype we have in production at Autodesk with this library
  • write proper test suite & setup guide
  • figure out how to deal with React Fiber situation - solution use react@next dependency

Engineering

  • Write unit tests for components
  • Extract & refactor lib into separate modules
  • Decide if view registry should be truly global or if it should be instanced with a provider component and passed via context
  • Convert to typescript #10
  • Add better error message if Provider is missing

Infra

  • Come up with a plan to verify we've got good browser support
  • Set up circle ci
    • Run unit tests
    • Deploy github pages automatically on green build
    • Deploy canary release automatically on green build
  • Set up code coverage measurement with codacy
  • Export pkg.module

Documentation

  • Write some sample applications in addition to the demos we've built so far.
  • Add a forkable example on https://glitch.com/ that's really easy to play with
  • Show an example using Redux

Bugs

Tech Debt

Development Experience

  • This is looking pretty good since we are just literally using the default CRA

Fills ordered by least recently mounted

Issue:

Fills are rendered in reverse order that they are mounted

Expected:

Fills are rendered in their order in the react tree

Example:

https://conscious-violet.glitch.me/
See that the order that you toggle survey/news affects their button order in the toolbar

I'm happy to open a PR if you can think of a way to calculate the order. My only thought was to base it on a timestamp/global counter since I don't think components have access to their siblings but that seems like a poor solution.

Ability to wrap if slot if filled

A common pattern is wanting to wrap a slot if it has something filling it, not sure if this is possible.

Potential syntax:

<Slot name="name">
  {children => <wrapper>{children}</wrapper>}
</Slot>

Also, does will this support the option to have a slot be able to be filled with many Fill components? Basically if I have 10 different components somewhere in the sub-tree, could I specify something like:

<Slot name="name" accepts="many">
  {children => <wrapper>{children}</wrapper>}
</Slow>

Where accepts = 'many' | 'one' | Function or something like this?

fillChildProps cannot pass functions through to fill

Passing functions through to fills is currently not possible. Instead they are called during the transform step:

const value = fillChildProps[key];

if (typeof value === 'function') {
    acc[key] = () => value(fill, this.fills);
} else {
    acc[key] = value;
}

This prevents us from passing callbacks like onChange into fills.

Reading the readme, this seems to be by design:

  /**
   * Props to be applied to the child Element of every fill which has the same name.
   *
   *  If the value is a function, it must have the following signature:
   *    (target: Fill, fills: Fill[]) => void;
   *
   *  This allows you to access props on the fill which invoked the function
   *  by using target.props.something()
   */
  fillChildProps?: {[key: string]: any}

Could you please help me understand why this is the case? If the main use case is to get a 'ref' to the fill, wouldn't it be nicer and less limiting to make that a separate property?

Propagate props into Slot's div wrapper.

Hi, Thanks for the lib. For React 15.4 you wrap Slot's fill with <div>, it'd be nice to propagate some props like style and className to that div. it's useful when we pass many fills in one slot to be able to align them with the styles.

Can't work since v1.0.0-alpha.2

I upgraded to v1.0.0-alpha.7 and my project didn't work, then I tried all versions, just v1.0.0-alpha.1 can worked. Now I'm using [email protected], I didn't test newer react version, because some modules removed from react since v16.0.0-alpha.7, some libraries can't build.

Error messages:

React caught an error thrown by Slot. You should fix this error in your code. React will try to recreate this component tree from scratch using the error boundary you provided, AppContainer.

TypeError: Cannot read property 'onComponentsChange' of undefined

The error is located at: 
    in Slot (at ShowDesign.js:24)
    in div (created by Menu)
    in Menu (at ShowDesign.js:20)
    in div (at ShowDesign.js:18)
    in ShowDesign (created by RouterContext)
    in div (at Show.js:202)
    in div (at Show.js:189)
    in div (at Show.js:146)
    in Show (created by inject-Show)
    in inject-Show (created by RouterContext)
    in div (at App.js:46)
    in App (created by inject-App-with-race-prompt-user)
    in inject-App-with-race-prompt-user (created by RouterContext)
    in RouterContext (created by Router)
    in Router (at Root.js:27)
    in DocumentTitle (created by SideEffect(DocumentTitle))
    in SideEffect(DocumentTitle) (at Root.js:37)
    in div (at Root.js:36)
    in Unknown (at Root.js:35)
    in Root (created by inject-Root-with-user)
    in inject-Root-with-user (at index.js:60)
    in Provider (at index.js:59)
    in ApolloProvider (at index.js:58)
    in AppContainer (at index.js:57)

The error was thrown at: 
    at Slot.componentWillMount (http://localhost:3001/static/js/bundle.js:169242:30),
    at mountClassInstance (http://localhost:3001/static/js/bundle.js:41368:17),
    at updateClassComponent (http://localhost:3001/static/js/bundle.js:39327:10),
    at beginWork (http://localhost:3001/static/js/bundle.js:39749:17),
    at performUnitOfWork (http://localhost:3001/static/js/bundle.js:38354:17),
    at workLoop (http://localhost:3001/static/js/bundle.js:38483:27),
    at HTMLUnknownElement.boundFunc (http://localhost:3001/static/js/bundle.js:25766:15),
    at invokeGuardedCallback (http://localhost:3001/static/js/bundle.js:25780:17),
    at invokeGuardedCallback (http://localhost:3001/static/js/bundle.js:25815:35),
    at performWork (http://localhost:3001/static/js/bundle.js:38525:20)

Getting "ReferenceError: mitt is not defined" in v1.0.0-alpha.11

Seems like the "module" target bundle doesn't import mitt, which results in the error "ReferenceError: mitt is not defined". Is this a bug or am I missing something?

I set up a simple example project and followed the usage notes. I use Webpack 2 for bundling. Please let me know if you need any additional info!

Thanks!

Provider only accepts a single child

Currently, the Provider component accepts only a single child:

render() {
  return React.Children.only(this.props.children);
}

This somewhat messes with our implementation, and also seems to render the example in the readme invalid:

import Toolbar from './Toolbar';
import Feature from './Feature';

import { Provider } from 'react-slot-fill';

const App = () =>
  <Provider>
    <Toolbar />
    <Feature />
  </Provider>

ReactDOMFiber.render(
  <App />,
  document.getElementById('root')
);

Did I misunderstand any of this? Is there a reason why the library would not allow multiple children for Provider?

error TS2554: Expected 1-2 arguments, but got 0

I am getting this error after cloning the project and running npm i and npm start. Seems like a dependency issue...what am I missing?

Failed to compile.

./src/lib/components/Provider.ts
(20,5): error TS2554: Expected 1-2 arguments, but got 0.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.