Git Product home page Git Product logo

slimjs / slim.js Goto Github PK

View Code? Open in Web Editor NEW
1.0K 25.0 66.0 2.9 MB

Fast & Robust Front-End Micro-framework based on modern standards

Home Page: http://slimjs.com

License: MIT License

HTML 1.09% JavaScript 26.05% CSS 69.05% TypeScript 2.44% Handlebars 1.37%
webcomponents data-binding es6 dom lifecycle javascript library slimjs front-end front-end-development framework angular react aurelia html5 html-element vuejs native

slim.js's Introduction

hello, slim.js

Build Status

Chat on gitter

Hello slim.js - your declarative web components library

import { Slim } from 'slim-js';
import { tag, template } from 'slim-js/decorators';

@tag('my-awesome-element')
@template(`
<button @click="this.inc()"> + </button>
<span>{{this.count}}</span>
<button @click="this.dec()"> - </button>
`)
class extends Slim {
  count = 0;
  inc() { this.count++ }
  dec() { this.count-- }
}

Slim is an ultra fast, native and elegant library for Web Components development

It's fast. Very fast.

Super fast. It leverages the native browser's API to manipulate DOM using templates, with custom directives. Imagine vue or angular's strctured templates combined with react's ease of use - combined, faster and lighter.

What's the difference? Why slim.js?

It's native. It has no black magic, no compilers and no external dependencies. It aims for fast performance, low CPU usage, using the native HTMLElement class inheritence and CustomElementsRegistry.

It works anywhere. You can combine slim.js in any other framework, combine any framework into slim.js app or any other combination.

You can write fully functional classes with complex code, or create pure "render-only" components without writing a single function.

Opt-in/Opt-out anything. The core is super small (2927 bytes gzipped), every directive is a self-contained module. Opt-in if you like. Pick your preferences for using (or not) shadow-dom. Work directly with properties on the element or create a view-model layer.

It's super easy to learn. Anything in your template that is wrapped with bars (example: <div>{{ this.user.name }}</div> ) is the actual code running. And it runs only when the used properties change. Changes affects only the specific DOM nodes that are bound. It means you can manipulate DOM manually without bothering the core, and vice-versa. No virtual dom engine or anything like that.

What about bundling, typescript, or other tools?

It just works. The library is written in javascript, and it has index.d.ts to support strongly-typed projects. It also provides access to it's internals, if you want to hack things out.

What are "directives" anyway?

Directives are middlewares that executes code on your template whenever you have attributes in your markup. For example, if you opt-in for the property-directive, any attribute that starts with a period (i.e. <some-element .user="{{this.user}}"></some-element>) will trigger the property-directive into action: this.user will project as a user property into some-element. Another example is the foreach-directive: You can map arrays or iterables to repeating elements. For example

<ul>
  <li *foreach="{{this.users.slice(0, 50)}}">
    <img src="{{item.picture}}" />
    <span class="user-name">{{item.name}}</span>
  </li>
</ul>

slim.js provides the following out-of-the-box directives:

  • custom-code (default)<form disabled="{{this.isFormReady(this.data)}}">...</form> or <div style="{{this.getStyleFor(this.user)}}">Welcome, {{this.user.name}}!</div>
  • property <img .src="{{this.imageSource}}">
  • events <input @change="this.handleInputChange(event)">
  • if <div *if="{{!this.isLoaded}}" class="spinner">Loading...</div>
  • foreach <li *foreach="{{this.someArray}}">{{item.value}}</li>
  • reference <form #ref="myForm">...</form> will create a property targeting the DOM element
  • repeat (optional, faster, with lazy memory release) <li *repeat>

All the directives (excluding custom-code) are optional - each is a standalone module. Choose the directives you use, or write your own!

What about plugins?

You can hook into any slim.js component's lifecycle using plugins. You plugin will be notified for step of the lifecycle, so you can add you own custom code or change things on-the-go.

Isn't a runtime library slower than compiled libraries?

Well, slim.js is an exception. Your custom code is memoized, and every piece of in-template code is created only once, can be even shared accross components. The core is very small and efficient, and releasing bound nodes is done progressively as background-tasks, keeping the user interface responsive as first priority. Modern browsers supports requestIdleCallback, so if supported, the removed elements will be released slowly and progressively.

Is this another framework?

Yes, and No. It's a very thin, fast, and extensible core library for writing custom-elements, powered by optional plugins and directives. You have the power to decide. It adds the firepower of a framework to your web-components, using the browser's native capabilities — as defined by W3C standards.

Here's what you get:

  • HTML markup with custom inlined code.
  • Reactive components: If a property is in the template, it becomes reactive. The core wraps it with a getter and setter function (and keeping your original one intact, if exists). No need to declare, it's derived from the tempalte.
  • Data binding: data changes triggers changes in the rendered HTML.
  • Directive system: Register your own directives.
  • Plugins: Add global lifecycle hooks for every slim.js component.
  • It feels like a framework (in a good way), but without the limits of a classic framework. It works everywhere, you can pick your own framework.
  • Low-footprint: it's less than 3KB gzipped. Directives are optional.
  • Single file for core functionality, and you're good to go.
  • No dependencies, everything is based on native browsers' API. Choose your own tools.
  • (Optional) Decorators for ES7/Next syntax, via Babel included.
  • Works with mixins from other component libraries, such as Polymer, out of the box.

Standards-compliant

  • ES6
  • Web Components V1
  • No transpiling or compilation required

No Setup required. It just works.

Use native (or bundlers) import statements, load from the CDN, or use the non-module (commonJS) file.

Documentation

The official website is built with Slim.js (with source maps). Check out the source code, see it for yourself.

Migration from older versions

Version 3 introduced the plugins.

Version 4 introduced the es modules.

Version 5 has new engine, directive system and plugin system.

es modules

import { Slim } from 'slim-js';

IIFE (no-modules)

<script src="slim-js/dist/index.legacy.js"></script>

Access a global Slim object.

HTML + Modules

<script type="module" src="slim-js/dist/index.js"></script>

Contibutors are welcome.

USE THE PLATFORM

Would you like to spend some good money on a good library and support the project? Contact [email protected].

slim.js's People

Contributors

alxlchnr avatar benkoshy avatar bennypowers avatar dependabot[bot] avatar eavichay avatar giraldoalberto avatar hankchiutw avatar idori avatar jbruni avatar maerteijn avatar manor avatar markfridman avatar milton-alvarenga avatar msaikaushik avatar murloc6 avatar pablopalacios avatar sheepfromheaven avatar shootermv avatar snuggs avatar timvdlippe avatar yavuztor 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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

slim.js's Issues

Data binding to SVG properties throws error

Hi,
I am new to SlimJs and trying to use the data binding for some SVG attributes.

Slim.tag(
	'maze-board',
	`<svg class="maze-grid" bind:width="maxWidth" bind:height="maxHeight"></svg>`,
	class MazeBoard extends Slim {
		onBeforeCreated() {
			this.maxWidth = 1000;
			this.maxHeight = 1000;
		}
	});

But I a m getting the following error:

Slim.js:990 Uncaught TypeError: Cannot assign to read only property 'width' of object '#'
at Slim.js:990
at Function.commit (Slim.js:386)
at Function.commit (Slim.js:414)
at HTMLElement._executeBindings (Slim.js:528)
at doRender (Slim.js:647)

Am I doing something wrong here?

ES6 version with good export

Is it possible to change the Slim.es6.js file ?
You are using an UMD module inside Slim.js
It could be better to export directly Slim class in Slim.es6.js, and add your UMD stuff during ES5 build.

nested datasource property in repeaters

consider the following template:

<ol>
  <li s:repeat="receipt.products">
    <span>{{data.name}}</span>
    <br>{{data.effects.length}}
    <ol>
      <li s:repeat="data.effects as effect">{{effect.name}}</li>
    </ol>
  </li>
</ol>

the data.effects.length prints correctly the number of elements, but the nested repeater does not see the data 😢

registerElement causes conflict with current webcomponents-lite.js polyfill in Firefox

I found the use of deprecated document.registerElement() caused slim.min.js to throw an error in the following function.

I fixed the issue by uncommenting the new window.customElements.define().

Is there a specific reason why this is uncommented in the first place? Thank you very much.

    /**
     * Declares a slim component
     *
     * @param {String} tag html tag name
     * @param {String|class|function} clazzOrTemplate the template string or the class itself
     * @param {class|function} clazz if not given as second argument, mandatory after the template
     */
    static tag(tag, clazzOrTemplate, clazz) {
        if (clazz === undefined) {
            clazz = clazzOrTemplate;
        } else {
            Slim.__templateDict[tag] = clazzOrTemplate;
        }
        Slim.__prototypeDict[tag] = clazz;
        window.customElements.define(tag, clazz);
        // document.registerElement(tag, clazz);
    }

Describe better how to get it running on IE11

I ended up needing a Reflect and Symbol polyfill, and it was an absolute pain getting it up and running (still need to do some debugging), but it would be good to document this better.

Notes: Only polyfill that ended up working for me was the one included in the babel-polyfill package. Directly including Reflect from both v5 and v6 of core-js didn't work, nor did harmony-reflect work for me.

Recursive Repeaters

I understood right, recursive repeaters are not possible?
this is the use case.

@tag('sws-navigation-item')
@useShadow(true)
@template(`
<ul>
    <li slim-repeat="items" slim-repeat-as="item">
        <a bind href="[[item.url]]">[[item.title.text]]</a>
        <sws-navigation-item interactive item="[[item.childLayer]]" click="openItem" id="[[item.id]]"></sws-navigation-item>
    </li>
</ul>
`)
export class NavigationItem extends Slim {}

Incorrect call to autoBoundAttributes.includes inside attributeChangedCallback prevents attribute-property syncing

When an attribute is set using element.setAttribute(..), its auto-bound property is not updated properly, due to an incorrect call for autoBoundProperties.includes.

if (newValue !== oldValue && this.autoBoundAttributes.includes[attr]) {

Since includes is a function, this.autoBoundAttributes.includes[attr] should be this.autoBoundAttributes.includes(attr)

Changes between 3xx and 4xx

Is there a list of changes / are they completely in switching to typescript or are there API changes as well? I got a ding from resolve bot about new version existing but I don't see anything mentioned about it here as far as release notes.

Thanks

Props passed to computed binding in s:repeat has no args

Hi again,
trying to use computed bindings inside a s:repeat. See line {{calcPercentage}}

@tag('custom-table')
@template(`
<div>
 <table>
    <tbody>
      <tr s:repeat="items as item">
        <th>
          <a click="navToEdit(item)" bind>{{item.name}}</a>
        </th>
        <td bind>{{item.status}}</td>
        <td bind>{{calcPercentage(item)}}%</td>
      </tr>
    </tbody>
  </table>
</div>`
export default class TableComponent extends Slim {
  items = []
  onBeforeCreated() {
    items = [
      { name: 'first', status: 'ok', num: 50 },
      { name: 'second', status: 'ok', num: 80 },
    ]
  }
  calcPercentage(item) {
    // item is 'undefined'
    return ( item.num / 100 ) * 100 // just an example of trying to access props in 'item'
  }
  ...
}

calcPercentage is not getting passed the item from s:repeat. item is undefined in the method. Why is item not passed to the function in the computed binding?

slimjs.com domain name does not resolve

dns not setup? I see the domain was updated in the past 24 hours, but even google doesn't have an IP for the domain

dig @8.8.8.8 slimjs.com

; <<>> DiG 9.2.4 <<>> @8.8.8.8 slimjs.com
; (1 server found)
;; global options:  printcmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 16042
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;slimjs.com.                    IN      A

;; Query time: 145 msec
;; SERVER: 8.8.8.8#53(8.8.8.8)
;; WHEN: Thu Nov  9 22:49:53 2017
;; MSG SIZE  rcvd: 28

How to do constructor injection?

I want to do custom templates through a by overwriting the template getter.
And I want to inject an EventManager (potentially I will inject other things as well)

I get a Illegal constructor TypeError if I do that.

example.ts

import { Slim } from "slim-js";
import { tag, useShadow, template } from "slim-js/Decorators";
import { TemplateProvider } from './../../utilities/templates'
import { EventManager} from './../../utilities/events'

@tag('sws-example')
@useShadow(false)
export class ExampleComponent extends Slim {

    public events: EventManager

    name: string;
    
    items: any[]
    
    public onBeforeCreated(): void {
        this.name = "example";
        this.items = [
            {name: 'Apple', id: 1},
            {name: 'Orange', id: 2},
            {name: 'Banana', id: 3}
        ];
    }

    public selectItem(event: Event): void {
        // invoked!
        let itemId = $(event.target).attr('id');
        alert('Selected item '+itemId);
    }
    
}

@tag('sws-item')
@useShadow(true)
@template(`
<button><slim-content></slim-content></button>
`)
export class ChildComponent extends Slim {

}

and in main.ts I create the component like this:

import { template } from 'slim-js/Decorators'
...


        template(templateProvider.templates['sws-example'])(ExampleComponent)
        let eC = new ExampleComponent()
// Setter injection trying alternatives
        eC.events = events

This error message I still get:

Slim.min.js:1 Uncaught TypeError: Illegal constructor
    at ExampleComponent._CustomElement (Slim.min.js:1)
    at ExampleComponent.Slim (Slim.min.js:1)
    at new ExampleComponent (example.ts:8)
    at main.ts:25
    at HTMLDocument.<anonymous> (zepto.js:446)

Is there any way to achieve something like this?

Thank you very much,

Tilman

Documentation update

It would be very helpful if documentation (at main site) contained a link to Slim Webpack Biolerplate project.

Also, Web Components polyfill at the Getting Started page references version 1.0.17 of webcomponents-lite.js, and the Boilerplate project references version 1.2.7, while the last version is 1.3.3. Does it make sense to update to this version?

Forbidden tag names

I get a SyntaxError: An invalid or illegal string was specified exception when I try to use

@tag('app')
@template('<p>App</p>')
@useShadow(true)
export default class App extends Slim {
  //
}

or

@tag('main')
@template('<p>App</p>')
@useShadow(true)
export default class App extends Slim {
  //
}

while

@tag('my-app')
@template('<p>App</p>')
@useShadow(true)
export default class MyApp extends Slim {
  //
}

works fine.

I don't understand why "app" and "main" tags cause exception, but if it can't be fixed it should at least be reflected in the documentation, I think, because these names are the first ones to come to mind for the whole application component. :)

Content transclusion

I'm not sure if this is a missing feature or just missing documentation. Is there any possibility to transclude the content inside a component into a slot inside the template or create a component without template that preserves the content inside?

Model passed in using 'create' plugin does not retain changes

I'm using the approach described here: http://slimjs.com/#/plugins to supply the component with data.
The model is just a JS object.
When my components make changes, these are not being reflected in the model. The components are working with the changes (for example, an s:if condition will react when the value of the property changes), but I don't see that in the original model. Am I missing something?

Here's my usage scenario:

@tag('parent-component')
@template(`
<child-component bind:items="parentProp.path.items"></child-component>
`
export default class ParentComp extends Slim {
  parentProp = {}
}

My child component has a property items and its template contains a list with s:repeat="items as item".
Before the parent-component element is added to DOM, I do:

let data = {
  path: {
    items: [ ... ]
  }
}
Slim.plugin('create', element => {
  if (element.localName === 'parent-component') {
    element.parentProp = data
  }
})

Could it be because the binding to items in the child is too deep? i.e. parentProp.path.items. I have other scenarios where the binding is shallower (e.g. parentProp.prop) and this works.

Bindings a broken after calling this.render()

I created a fiddle to show an error (or misunderstanding on my side :)).
After pressing the update button, which only calls this.render() I got following exception Cannot read property 'chain' of undefined.

Is that an issue or is it not intended to call render() within the component?

https://jsfiddle.net/wmoL2v81/66/ (I know the binding will be updated after the value is changed. In my real component, there is some logic in the onRender() method. That's the reason I thought calling this.render() is a smart idea :) )

Support shadyCSS

If Slim.__WCSupported is false, replace in style nodes ":host" with the tag name and unique identifier as custom attr

repeated data element cannot be bound when alias is used

works

  • i.e data is accessible in the click handler
<span
    s:repeat="ingredients" 
    bind:item="data"
    click="click"
  >{{data.name}}</span>

does't work

  • i.e data is NOT accessible in the click handler
<span
    s:repeat="ingredients as item" 
    bind:item="item"
    click="click"
  >{{item.name}}</span>

Scoped css

I am currently using Riot.js in my Web App Clibu and like the look off slim.js, especially as it doesn't require a compile step and child component access seems simpler.

Riot.js enables css to be specified along with the component markup, scoping it to the component.

I couldn't see any mention of css in your doc's - is this possible?

Support shadyCSS

Style encapsulation seems to be an important part missing in slim.js . Considering the "slim" nature I think it's good to leave it out by default, but an optional decorator allowing one to use it would be amazing.

It's fairly easy to 'trick' shadyCSS with

const templateEl = document.createElement('template');
templateEl.innerHTML = templateContent;

etc.

useShadow: accept the style string as is

Please consider the following form:

import {Slim} from "slim-js"
import {tag, template, useShadow} from "slim-js/Decorators"

@tag('my-tag')
@template(`
 <div role="main"> 
      my html 
 </div>
`)
@useShadow(`
  div[role="main"] {
      font-weight: bold
  }
`)
class _ extends Slim {}

Keep up the good work.
Thanks :)

Add slim.js to Benchmark

Hi,

I have created a benchmark suite for Web Components librairies. slim.js is implemented, and you can see the raw results here : vogloblinsky.github.io/web-components-benchmark

I will release soon a blog post explaining in detail that, but i want a feedback of many librairies authors before, to be sure i am not wrong in my implementations.

Source code here : vogloblinsky/web-components-benchmark

Can you have a look and give me some feedbacks ?

Thanks

Storybook components

Hi, I was wondering if slimjs could consider components with Storybook or simply is possible with Storybook for HTML (storybook allow reuse components in development time with elegant UI and drag and drop).
I think slimjs proposal is great. I can imagine mixed with Storybook would be so cool.
Thanks any way.

Deprecate v0 custom element reactions

bfcb464

If we are going to migrate to v1 registration I feel we can deprecate the hooks for the v0 callbacks (i.e. attached / detached) . Will make slim a touch slimmer. ;-) <3

Let me know if you need me to PR this.

fails when <style> tag in the end of the template

Consider a slim control with <style> tag in the end of the template.
Consider that an element before that <style> is a slim-control.
Consider the current control to have an input with an input event:

<span>
  <input s:id="queryInput" type="text" input="handleInput">
 </span>
 <child-slim-control s:repeat="somestuff"></child-slim-control>
<style>
 .foo{ color: blue }
</style>

When the input event is processed - I get:

Slim.min.js:1 Uncaught TypeError: Failed to execute 'appendChild' on 'Node': parameter 1 is not of type 'Node'.
    at HTMLElement._render (Slim.min.js:1)
    at HTMLElement.render (Slim.min.js:1)
    at HTMLElement.createdCallback (Slim.min.js:1)
    at init (Slim.min.js:1)

comes with log waring:

TypeError: Could not respond to event "input" on input -> "handleInput" on lab-search ... Failed to execute 'appendChild' on 'Node': parameter 1 is not of type 'Node'.
    at HTMLElement._render (Slim.min.js:1)
    at HTMLElement.render (Slim.min.js:1)
    at HTMLElement.createdCallback (Slim.min.js:1)
    at Slim.min.js:1
    at Array.forEach (<anonymous>)
    at Slim.min.js:1
    at Array.forEach (<anonymous>)
    at Slim.min.js:1
    at Function.commit (Slim.min.js:1)
    at Function.commit (Slim.min.js:1)

Repeatable Option Elements Not Rendering in Safari

I’m having some trouble using the slim-repeat and slim-repeat-as attributes with <option> elements in Safari.

Here’s an example…

Slim.tag('test-binding',
`
  <ul><li s:repeat="items" bind>{{data}}</li></ul>
  <select><option s:repeat="items" bind>{{data}}</option></select>
`,
class extends Slim {
  onBeforeCreated() {
    this.items = ['one', 'two', 'three'];
  }
});

When viewing this example in Safari, the <li> elements render correctly but there are no options available when interacting with the <select> element. The console does show the iterated elements but they are not accessible to the user (screenshot here).

This appears to be a problem for Safari on both iOS and OSX. Other major, modern browsers render both elements as expected.

Any thoughts? Thanks for your help.

Server Side Rendering

Hello,

I wanted to know if slim.js supports server side rendering. I have been looking at the documentation to see if this feature was available but I could not find any mention of it.

Thank you for your help and good job.

Linvi

Repeat directives nested

Hi,

Considering this array :

let list = [[1], [1,1], [1,2,1]]

How can i iterate to the elements ?

<div s:repeat="list as line">
    <triangle-item s:repeat="line as item" text="{{item}}"></triangle-item>
</div>

I correctly got the first list rendered, but inside each first line, i got <!--triangle-item s:repeat="line as item"-->

Thanks

Typo ?

Hi, I would like to know what is the purpose of having 2 same lines in the src file ?

Line: 855 self._boundChildren.push(child);
Line: 856 self._boundChildren.push(child);

Is it a typo ?

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.