Skip to content

luruke/kapla

 
 

Repository files navigation

kapla 👷‍

stability-wip NPM version Coverage Status

Tiny JS framework to manage DOM components

wip

Overview

The main goal is to make easier to implement common tasks/features:

  • Component declaration and instanciation
  • Component init and destroy (Barba.js ou load more…)
  • Access to $el et $refs
  • Use of data-attribute
  • Events handling with the right context (standard or custom)

Main features :

  • Components autoload + declaration data-component="foo"
  • Component lifecycle : load, init, destroy
  • Easy references through data-ref="foo.child" -> this.$refs.child
  • Replace dataset with simple API data-foo-prop="value" -> this.data.has('prop'), this.data.get('prop'), this.data.set('prop', 'another value')
  • Events binding/unbinding with onClick() {} ou onCustomEvent() {}

Start application

<main class="app"></main>
import { Application } from 'kapla/es';
import { autoLoad } from 'kapla/es/helpers';

import MyComponent from 'components/MyComponent';

const context = require.context('./components', true, /\.js$/);
const app = Application.start(qs('.app')); // If no element -> body

// Auto loading
app.load(autoLoad(context));
// Manual loading
app.register('my-component', MyComponent);

Use components

Basics

<div data-component="foo"></div>
<div data-component="sub--bar-baz"></div>
  • scripts/components/Foo.js
  • scripts/components/sub/BarBaz.js
import { Component } from 'kapla/es';

export default class extends Component {
    load() {}
    init() {}
    destroy() {}
}

Component filename must be PascalCase.js

References

<div data-component="foo">
    <button type="submit" data-ref="foo.submit">Submit</button>
</div>
this.$el // DIV
this.$refs.submit // BUTTON

Data

<div data-component="foo" data-foo-prop="qux"></div>
this.data.has('prop') // true
this.data.get('prop') // 'qux'
this.data.set('prop', 'quux') // 'quux'

Events

Native

Automatic binding/unbinding through lifecycle (init/destroy).

export default class extends Component {
    onClick(e) {}
    onBlur(e) {}
    
}
Mixed

Automatic binding/unbinding through lifecycle (init/destroy).

export default class extends Component {
    onEnter(e) {}
    onMove(e) {}
    onLeave(e) {}
}
Native/mixed + delegate
export default class extends Component {
    init() {
        this.delegateClick = 'selector';
        this.delegateMove = 'selector';
    }
    onClick(e, target) {}
    onMove(e, target) {}
    
}
Custom

Need to be 'registered' (before component registration).

import { myCustomEvent } from './events';

app.use('myCustomEvent', myCustomEvent);

Then, automatic binding/unbinding through lifecycle (init/destroy).

export default class extends Component {
    onMyCustomEvent(...args) {}
}
CustomEvent examples

Should have bind and unbind methods which receive component and ee as parameters. Can be 'scoped' to component (default) or global (see second example). In this case, you can choose to log the event name when it is emitted… Also, global custom events are binded only when components are listening to them. They are unbinded when no more components are listening to them.

export const clickOutside = {
  eventsByElement: new Map(),
  bind(component) {
    this.eventsByElement.set(component.context.element, this.listener(component));

    window.addEventListener('click', this.eventsByElement.get(component.context.element));
  },
  unbind(component) {
    window.removeEventListener('click', this.eventsByElement.get(component.context.element));
  },
  listener(component) {
    return function listener(e) {
      if (!component.context.element.contains(e.target)) {
        component.onClickOutside(e);
      }
    };
  },
};
export const raf = {
  scope: 'global',
  log: false,
  eventsByElement: new Map(),
  bind(component, ee) {
    this.ee = ee;
    this.eventsByElement.set(component.context.element, this.listener(component));

    this.ee.on('raf', this.eventsByElement.get(component.context.element));
    this.onTick = this.onTick.bind(this);
    this.time = window.performance.now();
    this.raf = window.requestAnimationFrame(this.onTick);
  },
  unbind() {
    window.cancelAnimationFrame(this.raf);
  },
  onTick(now) {
    this.time = now;
    this.delta = (now - this.oldTime) / 1000;
    this.oldTime = now;
    this.ee.emit('raf', this.delta, now);
    this.raf = window.requestAnimationFrame(this.onTick);
  },
  listener(component) {
    return function listener(delta, now) {
      component.onRaf(delta, now);
    };
  },
};
Manual

Native, mixed or custom events can be 'binded' or 'unbinded' manually.

export default class extends Component {
    method() {
        this.bind('click');
        this.bind('enter');
        this.bind('myCustomEvent');
    }
}

About

Tiny JS framework to manage DOM components

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • JavaScript 100.0%