UNPKG

14.6 kBMarkdownView Raw
1Composi
2=======
3
4Contents
5--------
6- [Installation](../README.md)
7- JSX
8 - [JSX](#JSX)
9 - [XML](#XML)
10 - [JSX Attributes](#JSX-Attributes)
11 - [Partial Attributes](#Partial-Attributes)
12 - [Custom Tags](#Custom-Tags)
13 - [Custom Tags with Spread Operator](#Custom-Tags-with-Spread-Operator)
14 - [One Tag to Rule Them All](#One-Tag-to-Rule-Them-All)
15 - [Fragment Tag](#Fragment-Tag)
16 - [Components with Same Container](#Components-with-Same-Container)
17 - [Using SVG Sprite Icons](#Using-SVG-Sprite-Icons)
18- [Hyperx](./hyperx.md)
19- [Hyperscript](./hyperscript.md)
20- [Functional Components](./functional-components.md)
21- [Mount, Render and Unmount](./render.md)
22- [Components](./components.md)
23- [State](./state.md)
24- [Lifecycle Methods](./lifecycle.md)
25- [Events](./events.md)
26- [Styles](./styles.md)
27- [Unmount](./unmount.md)
28- [State Management with DataStore](./data-store.md)
29- [Third Party Libraries](./third-party.md)
30- [Deployment](./deployment.md)
31- [Differrences with React](./composi-react.md)
32
33## JSX
34
35JSX provides a concise and convenient way to define markup to be created. Often people erroneously call JSX HTML. It is not HTML. It is in fact a type of XML. When you build your project, Babel takes the JSX code and converts it into hyperscript functions. In the case of a Composi project, it is set up to tell Babel to use Composi's hyperscript function for that transformation in the project's [.babelrc](https://babeljs.io/docs/usage/babelrc/) file.
36
37
38## XML
39
40Since JSX is a type of XML, tags need to follow XML rules. This means that all tags must be closed. In HTML 5 you can have self-closing tags, such as img, br, hr, input, etc.
41
42```html
43<img src='kitten.png'>
44<br>
45<hr>
46<input type='text' value='Cute Kitten'>
47```
48
49To use the above tags in JSX, they would need to be closed with a forward slash:
50
51```html
52<img src='kitten.png' />
53<br />
54<hr />
55<input type='text' value='Cute Kitten' />
56```
57
58Although some "purists" complain that JSX is mixing HTML into JavaScript, this is not completely true. JSX is just a DSL that describes the JavaScript functions that will be created to produce the elements. It is in fact very similar to a now abandoned effort to enable using XML in JavaScript called [E4X](https://developer.mozilla.org/en-US/docs/Archive/Web/E4X).
59
60If you read the E4X documentation, you will recognize the similarities to JSX. E4X was an attempt by Mozilla to enable the creation of DOM nodes without using string concatenation. Unfortunately E4X never took off. The introduction of template literals and tagged template literals solved some of the problems E4X was trying to address. However, the shortcoming of template literals is that the the markup is defined as strings. This means IDEs cannot understand the markup in a template literal. JSX does not have this limitation. As such text editors and IDEs provide great tooling to make using JSX productive.
61
62## JSX Attributes
63
64Unlike using JSX with React, Composi does not require that you use `className` instead of `class`. The following JSX would be invalid for React, but is the stardard for Composi:
65
66```javascript
67// Use class attribute as normal:
68function header(data) {
69 return (
70 <header>
71 <h1 class='title'>{data.title}</h1>
72 <h2 class='subtitle'>{data.subtitle}</h2>
73 </header>
74 )
75}
76```
77
78## Partial Attributes
79
80JSX does not support partial attribute values. The following code will not work:
81
82```javascript
83function userList(users) {
84 // The partial class defined below will not work:
85 return (
86 <ul>
87 {
88 users.map(user => (
89 <li class='currently-{user.employed ? "employed" : "unemployed"}'>{user.name}</li>)
90 )
91 }
92 </ul>
93 )
94}
95```
96
97The above JSX for the class value will generate an error. Instead you need to make the entire attribute value an evaluation. To do this, get replace the attribute value quotes with curly braces. Then use a template literal to output the results:
98
99```javascript
100function userList(users) {
101 // Calculate the entire class value inside curly braces:
102 return (
103 <ul>
104 {
105 users.map(user => <li class={`currently-${user.employed ? "employed" : "unemployed"}`}>{user.name}</li>)
106 }
107 </ul>
108 )
109}
110```
111
112**Note** When evaluating attribute values, never use quotes around the evaluation as this will prevent the evaluation from happening. Just use curly braces.
113
114## Custom Tags
115
116Although JSX makes it easy to create standard HTML elements, it also allows you to create custom tags. These are not the same as [custom element](). There may be a few, higher level similarities between these two. But fundamentaly they are for different purposes.
117
118A custom tag is really just a function that returns some JSX. When you want to use it in other JSX, you do so as a tag. Functions for custom tags must start with an uppercase letter, with no exception. When your JSX consists of many different parts, it makes sense to break it down into modular pieces.
119
120Suppose we have a component with a render function like this:
121
122```javascript
123const fruitsList = new Component({
124 container: '#fruit-list',
125 state: fruits,
126 render: (fruits) => (
127 <ul class='list'>
128 {
129 fruits.map(fruit =>
130 <li>
131 <div>
132 <h3>{fruit.title}</h3>
133 <h4>{fruit.subtitle}</h4>
134 </div>
135 <aside>
136 <span class='disclosure'></span>
137 </aside>
138 </li>
139 )
140 }
141 </ul>
142 )
143})
144```
145
146## Custom Tags with Spread Operator
147
148Looking at the above markup, we could break it up a bit to make it easier to read and reuse. To do this we'll define two functions to return markup. As mentioned earlier functions for custom tags must start with an uppercase letter. Lets break this up into two subcomponents:</p>
149
150```javascript
151// Define custom tag for fruit:
152function FruitItem({fruit}) {
153 return (
154 <div>
155 <h3>{fruit.title}</h3>
156 <h4>{fruit.subtitle}</h4>
157 </div>
158 )
159}
160
161// Define custom tag for disclosure tag:
162function Disclosure() {
163 return (
164 <aside>
165 <span class='disclosure'></span>
166 </aside>
167 )
168}
169```
170
171<p>In order to pass in the data, we need to encose the data parameter in curly braces. This is how props are passed to tags in JSX. When we actually use a custom tag, we need to use another convention with curly braces: double braces and a spread operator for destructuring assignment. This will pass each property of the object to the JSX function so that they can be accessed. It looks like this:
172
173```javascript
174 const data = {
175 prop1: 'Whatever',
176 prop2: 'More of the Same'
177 }
178 // Pass object with destructing to tag:
179 <MyTag {...{data}} />
180```
181
182Remember that custom tags need to be closed with a forward slash at the end.
183
184Below is how we can break our JSX into custom tags:
185
186```jsx
187function ListItem({fruit}) => (
188 <div>
189 <h3>{fruit.title}</h3>
190 <h4>{fruit.subtitle}</h4>
191 </div>
192)
193
194// No need for props here,
195// we're just returning markup:
196function Disclosure() => (
197 <aside>
198 <span class='disclosure'></span>
199 </aside>
200)
201
202// Now that we have some custom tags, we can use them as follows:
203
204const fruitsList = new Component({
205 container: '#fruit-list',
206 state: fruits,
207 // Use spread operator on ListItem:
208 render: (fruits) => (
209 <ul class='list'>
210 {
211 fruits.map(fruit => (
212 <li>
213 <ListItem {...{fruit} />
214 <Disclosure />
215 </li>
216 )
217 }
218 </ul>
219 )
220})
221```
222
223This results in cleaner code that is easier to read and maintain.
224
225## One Tag to Rule Them All
226
227Because of the way JSX works, there must always be one enclosing tag for all the other tags you wish to create. You cannot return a group of siblings. They need to be contained in another tag. For example, suppose you wanted to create some list items to insert in a list:
228
229```javascript
230// This will not compile:
231const badJSX = new Component({
232 container: '#list',
233 render: () => (
234 <li>One</li>
235 <li>Two</li>
236 <li>Three</li>
237 )
238})
239```
240
241The above code will not build. Instead you need to create the entire list like this and insert it in some higher element as the container:
242
243```javascript
244const goodJSX = new Component({
245 container: '#listDiv',
246 render: () => (
247 <ul>
248 <li>One</li>
249 <li>Two</li>
250 <li>Three</li>
251 </ul>
252 )
253})
254```
255
256## Fragment Tag
257
258As of version 1.5.0, Composi also supports a special Fragment tag. This allows you to group a number a siblings together instead of needing to enclose them in an html tag. The Fragment tag will be the parent, however it will not be converted into a tag in the DOM. Instead its children will become the children of whatever the Fragment gets inserted into. This is similar to how document fragments work. However, this is not an actual document fragment. The Fragment tag must always start with a capital F:
259
260```javascript
261function Title() {
262 return (
263 <Fragment>
264 <h1>Main Title</h1>
265 <h2>Secondary Title</h2>
266 </Fragment>
267 )
268}
269```
270
271Let's look at the previous list example to see how we can use Fragments to make it more manageable. Before we can use the Fragment tag, we need to import it into our project:
272
273```javascript
274import {h, Fragment, Component} from 'composi'
275
276class List extends Component {
277 container = '#listDiv'
278 state = ['Apples', 'Oranges', 'Bananas']
279 render(items) {
280 function ListItems({items}) {
281 return (
282 <Fragment>
283 {
284 items.map(item => <li>{item}</li>)
285 }
286 </Fragment>
287 )
288 }
289 return (
290 <ul>
291 <ListItems items={this.state}>
292 <ListItems>
293 </ul>
294 )
295 }
296}
297// Instantiate a new List:
298new List()
299```
300
301Because Fragment just returns their children, if you nest them, when you return them their children will be flattens. Notice what the following exaple returns:
302
303```javascript
304import {h, render, Fragment} from 'composi'
305
306const letters = ['A', 'B', 'C', 'D', 'E', 'F']
307function Items({letters}) {
308 return (
309 <main>
310 <Fragment>
311 <span>{letters[0]}</span>
312 <span>{letters[1]}</span>
313 <Fragment>
314 <span>{letters[2]}</span>
315 <span>{letters[3]}</span>
316 <Fragment>
317 <span>{letters[4]}</span>
318 <span>{letters[5]}</span>
319 </Fragment>
320 </Fragment>
321 </Fragment>
322 </main>
323 )
324}
325
326render(<Items letters={letters}/>, document.body)
327// This will create the following:
328<main>
329 <span>A</span>
330 <span>B</span>
331 <span>C</span>
332 <span>D</span>
333 <span>E</span>
334 <span>F</span>
335</main>
336```
337
338## Components with Same Container
339
340Components do not have to have unique container elements. Multiple components can be rendered in the same container element. Their order in the DOM will be dependent on their order in execution.
341
342## Using SVG Sprite Icons
343
344Often developers use SVG sprite sheets for icons in their apps. Here are some articles about how this works: [Icon System with SVG Sprites](https://css-tricks.com/svg-sprites-use-better-icon-fonts/), [An Overview of SVG Sprite Creation Techniques](https://24ways.org/2014/an-overview-of-svg-sprite-creation-techniques/), [How to Implement Cross-Browser SVG Sprites](https://webdesign.tutsplus.com/tutorials/how-to-implement-cross-browser-svg-sprites--cms-22427)
345
346### SVG 1 and SVG 2
347
348SVG 1.0 uses the `xlink:ref` attribute to link to an icon id in an SVG sprite sheet image. Unfortunately, JSX does not support namespaced properties on SVG. To get arround this limitation, Composi lets you use a custom property: `xlink-href` in your SVG icons. At render time this gets converted to the correct form as `xlink:href`. Although `xlink:href` is currently listed as deprecated for Firefox, Chrome and Edge, it is widely supported on older browsers. In fact, it is currenlty the only way to implement SVG icons on macOS and iOS. If you are not targetting macOS and iOS and do not care about older versions of IE, Firefox and Chrome, you can use the new syntax for SVG 2.0. This is a simple `href` property.
349
350
351Below is an SVG Twitter image that we will use as the basis for a series of SVG icons of different colors. Notice that we have a path with the id of "shape-twitter". We'll use that id to pull that path into our icon.
352
353```html
354<svg class="hide" style="display:none;">
355 <g id="shape-codepen">
356 <path id="shape-twitter" d="M100.001,17.942c-3.681,1.688-7.633,2.826-11.783,3.339
357 c4.236-2.624,7.49-6.779,9.021-11.73c-3.965,2.432-8.354,4.193-13.026,5.146C80.47,10.575,75.138,8,69.234,8 c-11.33,0-20.518,9.494-20.518,21.205c0,1.662,0.183,3.281,0.533, 4.833c-17.052-0.884-32.168-9.326-42.288-22.155
358 c-1.767,3.133-2.778,6.773-2.778,10.659c0,7.357,3.622,13.849,9.127, 17.65c-3.363-0.109-6.525-1.064-9.293-2.651
359 c-0.002,0.089-0.002,0.178-0.002,0.268c0,10.272,7.072,18.845,16.458,20.793c-1.721,0.484-3.534,0.744-5.405,0.744
360 c-1.322,0-2.606-0.134-3.859-0.379c2.609,8.424,10.187,14.555,19.166,14.726c-7.021,5.688-15.867,9.077-25.48,9.077
361 c-1.656,0-3.289-0.102-4.895-0.297C9.08,88.491,19.865,92,31.449,92c37.737,0,58.374-32.312,58.374-60.336
362 c0-0.92-0.02-1.834-0.059-2.743C93.771,25.929,97.251,22.195,100.001,17.942L100.001,17.942z"></path>
363 </g>
364</svg>
365```
366
367This image needs to be loaded into the document so that it is exposed globally. Since we put `style="display:none:"` on the SVG tag, we don't have to worry about it showing up anywhere. To use this, we can do the following:
368
369```javascript
370const icons = new Component({
371 container: 'section',
372 render: (data) => {
373 return (
374 <div>
375 <svg viewBox="0 0 100 100" class="icon--shape-twitter-1">
376 <use xlink-href="#shape-twitter"></use>
377 </svg>
378
379 <svg viewBox="0 0 100 100" class="icon--shape-twitter-2">
380 <use xlink-href="#shape-twitter"></use>
381 </svg>
382
383 <svg viewBox="0 0 100 100" class="icon--shape-twitter-3">
384 <use xlink-href="#shape-twitter"></use>
385 </svg>
386
387 <svg viewBox="0 0 100 100" class="icon--shape-twitter-4">
388 <use xlink-href="#shape-twitter"></use>
389 </svg>
390 </div>
391 )
392 }
393})
394icons.update()
395```
396
397Notice that we gave each SVG tag a unique class. We can use these to give each icon a different color:
398
399```css
400svg * {
401 transition: all .5s ease-out;
402}
403.shape-twitter-1 {
404 fill: #000
405}
406.shape-twitter-2 {
407 fill: #55ACEE
408}
409.shape-twitter-3 {
410 fill: #ff0000;
411}
412.shape-twitter-4 {
413 fill: #00aa00;
414}
415.shape-twitter-1:hover,
416.shape-twitter-2:hover,
417.shape-twitter-3:hover,
418.shape-twitter-4:hover {
419 fill: #0000ff;
420}
421```