Giter VIP home page Giter VIP logo

material-remixer-js's Introduction

Remixer for JavaScript

Build Status codecov npm version Chat

Remixer is a framework to iterate quickly on UI changes by allowing you to adjust UI variables without needing to rebuild (or even restart) your app. You can adjust Numbers, Colors, Booleans, and Strings. To see it in action check out the example app.

If you are interested in using Remixer in another platform, you may want to check out the iOS and Android repos. With any of the three platforms you can use the Remote Controller to change the variables from a web dashboard.

Using Remixer in your app

1. Use npm to install as dependency.

npm install material-remixer --save

This will install the Remixer files in your project's node_modules folder.

2. Include the remixer.js script in your app.

<script src="./node_modules/material-remixer/dist/remixer.js"></script>

3. Begin by starting Remixer.

remixer.start();

4. (Optional) Configure the Web Remote Controller

This optional step is only needed if you wish to use the Web Remote Controller. If so, follow these guidelines:

  • Set up a new or existing Firebase account as detailed in the Web Remote Controller repository.

  • Add your Firebase account credentials to your app, and forward the param to the remixer.start() method.

    // Replace with your project's Firebase info.
    var config = {
      apiKey: "<API_KEY>",
      authDomain: "<PROJECT_ID>.firebaseapp.com",
      databaseURL: "https://<DATABASE_NAME>.firebaseio.com",
    };
    
    // Pass the config params to Remixer start method.
    remixer.start(config);
  • You can then toggle on/off sharing to the remote controller from within the Remixer overlay.

5. Add variables.

Now you can add any desired variables and use the callback method to assign the selectedValue property.

// Add a boolean variable to generate a toggle switch in the overlay.
remixer.addBooleanVariable("show", true, function(variable) {
  document.getElementById("box").style.display = variable.selectedValue ? "block" : "none";
});

API Documentation

Contributing to Remixer

We're excited you want to contribute to the project! Please read these docs so we can get your contributions submitted quickly.

Is material-foundation affiliated with Google?

Yes, the material-foundation organization is one of Google's new homes for tools and frameworks related to our Material Design system. Please check out our blog post Design is Never Done for more information regarding Material Design and how Remixer integrates with the system.

License

© Google, 2016. Licensed under an Apache-2 license.

material-remixer-js's People

Contributors

appsforartists avatar chriscox 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

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

material-remixer-js's Issues

Add new remote share menu

New collapsable menu container.

Allows:

  • Enable/Disable remote controller
  • Provides clickable link to remote controller url

Adds key `variables` to localStorage schema

Adds variable key to localStorage schema as follows:

__remixer__:
    variables: {
        key1: ...
        key2: ...
    }

This will allow adding preferences and firebase remoteId to the same __remixer__ key as needed.

Replace mdl-list with flexbox

Refactor the list of variable controls in the overlay in order to better control the layout. There are too many overrides to wrangle both mdl-list and mdl-list-item to the styling we need for controls.

Allow MDL components to be updated remotely.

Provide a reference to the MDL component, and call required methods as needed to update their state. This happens when a component is updated programmatically rather then physically via UI.

Consider replacing node dependencies in iframe with source from CDNs

Remixer has a few dependencies in the iframe overlay, such as Firebase, MDL, and React. They are pulled in via running npm install. However this makes using remixer in online tools such as codepen difficult, as you cannot simply import remixer by itself, and instead need to include all the dependencies.

As a suggestion, lets consider instead pulling in these dependencies via their recommended hosted CDNs from within the iframe.

We don't need to worry about performance here, as the iframe loads in background not needed immediately at page startup.

For reference, the iframe calling code is here.

Suggested CDNs:

Firebase
<script src="https://www.gstatic.com/firebasejs/3.6.9/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/3.6.9/firebase-database.js"></script>

MDL
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
<link rel="stylesheet" href="https://code.getmdl.io/1.3.0/material.min.css">
<script defer src="https://code.getmdl.io/1.3.0/material.min.js"></script>

React
<script src="https://unpkg.com/react@15/dist/react.min.js"></script>
<script src="https://unpkg.com/react-dom@15/dist/react-dom.min.js"></script>

Support for React Native

I think this library is built for browser only(React). I would like to use it in ReactNative. I've tried to use this in ReactNative. I'm getting Can't find variable: self. When I turn on "Debug JS Remotely" I'm getting document is not defined. If possible add support for React Native.

Make React architecture modular + unidirectional

I'm leaving my architecture notes from #8 in this issue, but it could probably be broken into smaller issues.

In React, components should be small/single responsibility and only affect themselves (through configuration, their children).

For instance, DOMController currently does a bunch of things:

  • Draws the UI scaffolding
  • Determines which route to show
  • Toggles the visibility of its parent element
  • Sets up keybindings

These could be broken into separate elements:

class RemixerRouter extends React.Component {
  state = {
    showOptions: false,
  }

  // Use an arrow function to get correct `this` binding
  toggleOptions = () => {
    this.setState(
      {
        showOptions: !this.state.showOptions,
      }
    );
  }

  render() {
    return (
      <RemixerPanel
        toggleOptions = { this.toggleOptions }
      >
        {
          this.state.showOptions
            ? <Options />
            : <VariableConfigurator />
        }
      </RemixerPanel>
    )
  }
}

function RemixerPanel(props) {
  return (
    <div className = "rmx-card-wide mdl-card mdl-shadow--6dp">
      <div className = "mdl-card__title">
        <h2 className = "mdl-card__title-text">
          Remixer
        </h2>
      </div>

      <div className = "mdl-card__menu">
        <button 
          className = "mdl-button mdl-button--icon mdl-js-button mdl-js-ripple-effect"
          onClick = { props.toggleOptions }
        >
            <i className = "material-icons">
              menu
            </i>
        </button>
      </div>

      <div className = "mdl-card__supporting-text mdl-card__actions mdl-card--border">
        { children }
      </div>
    </div>
  );
}

function Cloak(props) {
  return props.hide
    ? null
    : props.children;
}

I'm going to move the global event listening out of a component and into the top level. This is a common pattern in React: to have all your shared state at the top-level, passed down via props.

let state = {
  showRemixer: false,
};

function toggleVisibility() {
  state = Object.assign(
    {}
    state,
    {
      showRemixer: !state.showRemixer
    }
  );

  rerender();
}

function rerender() {
  ReactDOM.render(
    <Cloak
      hide = { !state.showRemixer }
    >
      <RemixerRouter/>
    </Cloak>,
    container
  );
}

rerender();

function onKeyDown(event) {
  switch (event.keyCode) {
    case KeyCode.ESC:
      toggleVisibility();
      break;
  }
}

function onMessage(event) {
  switch (event.data) {
    case MessageType.TOGGLE_VISIBILITY:
      toggleVisibility();
      break;
  }
}

document.addEventListener(KeyboardEventType.DOWN, onKeyDown);
Messaging.register(onMessage);

For such a simple example, this is probably fine. However, as the app grows, you might find you want more order. That's where Redux comes in. It's this same pattern, but organized:

Redux is conceptually really simple. You have a high-level state atom (effectively just a tree of data you want to keep track of). Yours might be {showRemixer: true, showOptions: false}. Then, you have reducers that operate on that state. You might have this:

export function toggleVisibility(oldState = {showRemixer: false}, action) {
  switch (action.type) {
    case MessageType.TOGGLE_VISIBILITY:
      return (
        {
          ...oldState,
          showRemixer: !oldState.showRemixer
        }
      );
  }
}

The messages you are currently sending are called "actions" in Redux parlance. Every time it receives one, it calls the reducer to get an updated state. It then passes that updated state into your component tree (Cloak in our example above) to trigger a rerender. As you can see, this is the same thing I've done in the top-level of the example, just a bit more structured.

Imagine you wanted to add a keybinding to toggle the options. You could do so with a reducer that looked like this:

export function toggleOptions(oldState = {showOptions: false}, action) {
  switch (action.type) {
    case MessageType.TOGGLE_OPTIONS:
      return (
        {
          ...oldState,
          showOptions: !oldState.showOptions
        }
      );
  }
}

Your router simplifies to just this:

function RemixerRouter(props) {
  return (
    <RemixerPanel>
      {
        props.showOptions
          ? <Options />
          : <VariableConfigurator />
      }
    </RemixerPanel>
  );
}

RemixerPanel uses the toggleOptionsAction to handle clicks on the menu button, and your key listener calls it whenever someone hits the ? key. Redux would pass showOptions into RemixerRouter as a prop.

Redux is a 7K dependency. Since this is a developer tool, that's probably not an impactful number. For cleanliness/maintainability, it may be worth adopting.

Use named exports in constants

I'd split your constants into areas of responsibility and use named export to move them between files. For instance, in constants.ts:

export const KeyCode = {
  ESC: 27,
};

Then, you'd use KeyboardEventType.DOWN and KeyCode.ESC.

Use import/export statements rather than <script> tags for dependencies

Remixer has very strong opinions about how it's hosted: It must be served from a folder called node_modules, with its dependencies (e.g. ./node_modules/react-dom/dist/react-dom.min.js) as peers. It is also written as a global on window.

This makes it hard for experienced JavaScript developers to use. Even though it's written in TypeScript, I have to declare const remixer: any to use the global name, which loses all type information.

As a JavaScript developer, I expect to be able to all my dependencies to be imported with import statements. <script> tags with hardcoded URLs being injected isn't cool - it circumvents my bundler and potentially duplicates dependencies like React that are already in my bundle.

I suspect you tried to expose everything in a single script tag to make life easy for newcomers, but as a newcomer, it's actually made it a lot harder for me to try Remixer. I've probably spent half an hour trying to get it to work, and I'm giving up. Even after making a package.json in the root of my static site folder and running yarn add material-remixer, I'm getting errors about node_modules/material-overlay being mis-served, and I can't even find that folder.

Remixer seems like a really nice way to be able to quickly add a console to a UI. Unfortunately, its workflow is so foreign that I can't use it.

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.