Skip welcome & menu and move to editor
Welcome to JS Bin
Load cached copy from
 
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <title>Reactive web component example with subclassing</title>
  <template id="template">
    <button id="decrement">-</button>
    <span id="visibleValue"></span>
    <button id="increment">+</button>
  </template>
  <script type="module">
    import AttributeMarshallingMixin from 'https://cdn.rawgit.com/elix/elix/master/src/AttributeMarshallingMixin.js';
    import { merge } from 'https://cdn.rawgit.com/elix/elix/master/src/updates.js';
    import RenderUpdatesMixin from 'https://cdn.rawgit.com/elix/elix/master/src/RenderUpdatesMixin.js';
    import ReactiveMixin from 'https://cdn.rawgit.com/elix/elix/master/src/ReactiveMixin.js';
    import ShadowTemplateMixin from 'https://cdn.rawgit.com/elix/elix/master/src/ShadowTemplateMixin.js';
    import symbols from 'https://cdn.rawgit.com/elix/elix/master/src/symbols.js';
    // Create a native web component with reactive behavior.
    const Base =
      AttributeMarshallingMixin(
      ReactiveMixin(
      RenderUpdatesMixin(
      ShadowTemplateMixin(
        HTMLElement
      ))));
    class IncrementDecrement extends Base {
      componentDidMount() {
        if (super.componentDidMount) { super.componentDidMount(); }
        this.$.decrement.addEventListener('click', () => {
          this.value--;
        });
        this.$.increment.addEventListener('click', () => {
          this.value++;
        });
      }
      // This property becomes the value of this.state at constructor time.
      get defaultState() {
        return Object.assign({}, super.defaultState, {
          value: 0
        });
      }
      // Udpates represent property/attribute assignments to apply to the
      // component host and elements in its shadow subtree during rendering of
      // the current state. This is part of a rendering pipeline:
      //
      //     events -> methods/properties -> state -> render
      //
      get updates() {
        return {
          style: {
            color: this.state.value < 0 ? 'red' : null
          },
          $: {
            visibleValue: {
              textContent: this.state.value
            }
          }
        }
      }
      // Provide a public property that gets/sets state.
      get value() {
        return this.state.value;
      }
      set value(value) {
        this.setState({
          value: parseInt(value)
        });
      }
      get [symbols.template]() {
        return template;
      }
    }
    customElements.define('increment-decrement', IncrementDecrement);
    class CustomIncrementDecrement extends IncrementDecrement {
      get updates() {
        const base = super.updates;
        const baseColor = base.style && base.style.color;
        // Pick a color if the base class didn't specify one.
        const color =  baseColor || (this.state.value > 0 ? 'dodgerblue' : null);
        const buttonStyle = {
          background: '#444',
          border: 'none',
          'border-radius': 0
        };
        const decrementDisabled = this.state.value <= -5;
        const incrementDisabled = this.state.value >= 5;
        // Merge updates on top of those defined by the base class. This lets us
        // preserve some of the base rendering, while adding our own styling and
        // some unique behavior.
        return merge(super.updates, {
          style: {
            background: 'lightgray',
            color,
            'font-family': 'Helvetica, Arial, sans-serif',
            'font-weight': 'bold'
          },
          $: {
            decrement: {
              attributes: {
                disabled: decrementDisabled
              },
              style: merge(buttonStyle, {
                color: decrementDisabled ? 'gray' : 'white'
              })
            },
            increment: {
              attributes: {
                disabled: incrementDisabled
              },
              style: merge(buttonStyle, {
                color: incrementDisabled ? 'gray' : 'white'
              })
            }
          }
        });
      }
      get value() {
        return super.value;
      }
      set value(value) {
        // Enforce a bound of -5 to 5 on the value.
        const parsed = parseInt(value);
        const bounded = Math.max(Math.min(parsed, 5), -5);
        super.value = bounded;
      }
    }
    customElements.define('custom-increment-decrement', CustomIncrementDecrement);
  </script>
</head>
<body>
  <p>
    This shows a plain increment/decrement component and a subclass with styling
    and custom behavior.
  </p>
  <increment-decrement></increment-decrement>
  <custom-increment-decrement></custom-increment-decrement>
</body>
</html>
Output 300px

You can jump to the latest bin by adding /latest to your URL

Dismiss x
public
Bin info
JanMiksovskypro
0viewers