├── .gitignore
├── .npmignore
├── .prettierignore
├── .travis.yml
├── Dockerfile
├── LICENSE
├── Makefile
├── README.md
├── browsersync.config.js
├── examples
├── index.html
└── index.js
├── karma.conf.js
├── package-lock.json
├── package.json
├── rollup.config.js
├── src
└── gluon.js
└── test
├── browser.html
├── index.html
├── index.js
├── test-$.js
└── test-is.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | examples/index.es5.js
3 | test/index.es5.js
4 | gluon.*
5 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | Makefile
2 | Dockerfile
3 | src
4 | test
5 | index.html
6 | .prettierignore
7 | .travis.yml
8 | browsersync.config.js
9 | rollup.config.js
10 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | package.json
2 | package-lock.json
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js: node
3 |
4 | install: npm install && npm run build
5 | script: npm test
6 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ruphin/webserve
2 |
3 | COPY . /usr/share/nginx/html
4 | COPY ./node_modules/lit-html /usr/share/nginx/html/lit-html
5 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Goffert van Gool
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | .PHONY: dev
2 | dev:
3 | docker run -it --rm -v $$PWD:/app -p 5000:5000 ruphin/webdev npm run dev
4 |
5 | .PHONY: shell
6 | shell:
7 | docker run -it --rm -v $$PWD:/app ruphin/webdev bash
8 |
9 | .PHONY: test
10 | test:
11 | docker run -it --rm -v $$PWD:/app ruphin/webdev npm run test
12 |
13 | .PHONY: guard
14 | guard:
15 | docker run -it --rm -v $$PWD:/app ruphin/webdev npm run guard
16 |
17 | .PHONY: build
18 | build:
19 | docker run -it --rm -v $$PWD:/app ruphin/webdev npm run build
20 |
21 | .PHONY: release
22 | release: build
23 | docker run -v $$PWD:/app \
24 | -v $$HOME/.gitconfig:/home/app/.gitconfig \
25 | -v $$HOME/.npmrc:/home/app/.npmrc \
26 | -v $$HOME/.ssh:/home/app/.ssh \
27 | -it --rm ruphin/webdev npm run release
28 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Gluonjs
2 |
3 | [](https://travis-ci.org/ruphin/gluonjs)
4 | [](https://www.npmjs.com/package/@gluon/gluon)
5 | [](https://github.com/prettier/prettier)
6 |
7 | _A lightweight library for building web components and applications_
8 |
9 | ---
10 |
11 | - **Platform Based:** GluonJS is designed to leverage the latest web platform capabilities, making it extremely small in size, and very performant on modern browsers. Additionally, it means that **build/compile steps are optional**; GluonJS components work on modern browsers without any pre-processing.
12 | - **Component Model:** Build components with encapsulated logic and style, then compose them to make complex interfaces. Uses the Web Component standards, with all related APIs available directly to developers.
13 | - **Highly Reusable:** Because GluonJS creates standards-compliant Web Components, you can use components created with GluonJS in almost any existing application. Check [Custom Elements Everywhere](https://custom-elements-everywhere.com/) for up-to-date compatibility tables with existing frameworks.
14 | - **Powerful Templating:** GluonJS uses [lit-html](https://github.com/PolymerLabs/lit-html) for templating, making it highly expressive and flexible.
15 |
16 | ## Concepts
17 |
18 | ###
19 |
20 | ```javascript
21 | import { GluonElement } from '/node_modules/@gluon/gluon/gluon.js';
22 |
23 | class MyElement extends GluonElement {
24 | // ...
25 | }
26 |
27 | customElements.define(MyElement.is, MyElement);
28 | ```
29 |
30 | ### Rendering
31 |
32 | Gluon uses [lit-html](https://github.com/PolymerLabs/lit-html) to efficiently render DOM into elements. The template to render is defined in the `template()` getter of an element, using JavaScript tagged template literals.
33 |
34 | If a `template` is defined, Gluon will render the template during the initialization of the element (when `super.connectedCallback()` is called).
35 |
36 | ## API
37 |
38 | ### $
39 |
40 | All nodes in the template with an `id` attribute are automatically mappped in the `$` property of an element. This provides an easy way to access named nodes without `querySelector` or `getElementById`.
41 |
42 | The map is created during the initial render of the template. Use `this.shadowRoot.getElementById()` to access nodes that are added after the initial render.
43 |
44 | ### static get is()
45 |
46 | Returns a kebab-cased version of the element ClassName, as an easy default tagname for the customElementRegistry. This allows easy Custom Element registration using `customElements.define(MyElement.is, MyElement)`.
47 |
48 | \*\* NOTE: JavaScript minifiers like es-uglify may break this feature. Use the `{ keep_fnames: true, mangle: {keep_fnames: true} }` options in es-uglify to avoid breaking this feature. Alternatively, override the method to return a string to define a fixed tagname: `
49 |
50 | ‡ Pending IE support in [lit-html](https://github.com/PolymerLabs/lit-html)static get is() { return 'my-element' }`.
51 |
52 | ### get template()
53 |
54 | ### render()
55 |
56 | Calling `render()` on an element will queue a render at the next [microtask timing](https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/). Multiple calls are automatically batched into a single render.
57 |
58 | Returns a promise object that is fulfilled after the render is complete.
59 |
60 | To render synchronously, `render({sync: true})`
61 |
62 | ## Common Patterns
63 |
64 | ### Defining properties with getters and setters
65 |
66 | This basic pattern works by defining a property getter and setter that wrap some other storage location for the property value.
67 |
68 | ```javascript
69 | get someProp() {
70 | return this._someProp;
71 | }
72 |
73 | set someProp(value) {
74 | this._someProp = value;
75 | }
76 | ```
77 |
78 | Defining properties with getters and setters has no benefits in itself, but it makes for a more flexible system when adding more features such as property defaults, synchronising between properties and attributes, typed properties, or observing property changes, examples of which are listed below.
79 |
80 | ### Computed properties
81 |
82 | Computed properties can be created by defining a property getter that computes the value for the property.
83 |
84 | ```javascript
85 | get computedProp() {
86 | return this.someProp + this.otherProp;
87 | }
88 | ```
89 |
90 | \*\* NOTE: Computed properties are re-computed for every reference to the property. When the computation is expensive, it may be worthwhile to implement a cache:
91 |
92 | ```javascript
93 | get computedProp() {
94 | if (this.__previousSomeProp == this.someProp && this.__previousOtherProp === this.otherProp) {
95 | return this.__cachedComputedProp;
96 | }
97 |
98 | this.__previousSomeProp = this.someProp;
99 | this.__previousOtherProp = this.otherProp;
100 | this.__cachedComputedProp = this.someProp + this.otherProp;
101 | return this.__cachedComputedProp;
102 | }
103 | ```
104 |
105 | ### Typed properties
106 |
107 | Define typed properties by adding type coercion in the property getter or setter.
108 |
109 | ```javascript
110 | get numberProperty() {
111 | return this._numberProperty;
112 | }
113 |
114 | set numberProperty(value) {
115 | this._numberProperty = Number(value);
116 | }
117 | ```
118 |
119 | ### Synchronising properties and attributes
120 |
121 | ### Observing attribute changes
122 |
123 | Observing attribute changes is done using the Web Component attribute observer standards.
124 |
125 | To observe changes on attributes, define a `static get observedAttributes()` on the class that returns an array of all attributes to observe:
126 |
127 | ```javascript
128 | static get observedAttributes() {
129 | return ['some-attr', 'other-attr']
130 | }
131 | ```
132 |
133 | With this defined, `attributeChangedCallback(attr, oldValue, newValue)` will be called for all attributes listed in the array.
134 |
135 | ```javascript
136 | attributeChangedCallback(attr, oldValue, newValue) {
137 | if (attr === 'some-attr') {
138 | // some-attr changed
139 | } else if (attr === 'other-attr') {
140 | // other-attr changed
141 | }
142 | }
143 | ```
144 |
145 | \*\* NOTE: attributeChangedCallback is also called for the initial attributes set on an element. It is in fact called before the `connectedCallback()`, which means the template has not yet been rendered. If you need to interact with child nodes, use the promise returned by `render()` to guarantee the template has been rendered and the child nodes exist:
146 |
147 | ```javascript
148 | attributeChangedCallback(attr, oldValue, newValue) {
149 | if (attr === 'some-attr') {
150 | this.render().then( () => this.$.child.someFunction() );
151 | }
152 | }
153 | ```
154 |
155 | ### Observing property changes
156 |
157 | Observing property changes is done by calling the observer at the end of the property setter function.
158 |
159 | ```javascript
160 | get someProp() {
161 | return this._someProp;
162 | }
163 |
164 | set someProp(value) {
165 | this._someProp = value;
166 | this.somePropChanged();
167 | }
168 | ```
169 |
170 | ## Examples
171 |
172 | Here is an example of a GluonJS component:
173 |
174 | ```javascript
175 | // helloMessage.js
176 | import { GluonElement, html } from '/node_modules/@gluon/gluon/gluon.js';
177 |
178 | class HelloMessage extends GluonElement {
179 | get template() {
180 | return html`