- Notifications
You must be signed in to change notification settings - Fork48
nicojs/typed-html
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
HTML templates have never been this easy. Type safe using plain TypeScript with a minimal runtime footprint.No need to learn a template language, if you know TypeScript, you're set.
This:
// example.tsxconstitem='item';consticon='icon-add';constul=<ul><li>{item}</li></ul>;typeoful;// stringconstbutton=<buttononclick="handleClick"><iclass={icon}></i></button>;typeofbutton;// stringconsole.log(ul);console.log(button);
Prints:
<ul><li>item</li></ul><buttononclick="handleClick"><iclass="icon-add"></i></button>
Install:
npm install --save typed-html
Configure your TypeScript compiler for JSX:
{"compilerOptions": {"jsx":"react","jsxFactory":"elements.createElement" }}
Although we're configuring the compiler to useReact, this is not what is being used.Instead, we redirect all jsx element to typed-html'selements.createElement
.
Now create a *.tsx file. For example:example.tsx
with the following content:
// example.tsximport*aselementsfrom'typed-html';constw='world';consthelloWorld=<p>Hello<strong>{w}</strong></p>;typeofhelloWorld;// => Just a string of course
However, the following piece of code willNOT compile:
<foo></foo>;// => Error: Property 'foo' does not exist on type 'JSX.IntrinsicElements'.<afoo="bar"></a>;// => Error: Property 'foo' does not exist on type 'HtmlAnchorTag'
Typed HTML supports both NodeJS and (since 2.0) the browser.
For use in the browser, either load typed-html as a module, or use a bundler like webpack or rollup to bundle the package for you.
// Direct ES import:import*aselementsfrom'./node_modules/typed-html/dist/elements.js';// OR, when using a bundler like rollup or webpackimport*aselementsfrom'typed-html';
All template scenarios are supported with plain TypeScript.
Conditional template with?
<div>Random>0.5:{Math.random()>.5 ?<strong>yes</strong>:'no'}</div>
Repeat a template withArray.map
constitems=['item','item2'];<ul>{items.map(i=><li>{i}</li>)}</ul>;
Want a helper template? Just call a function
functionlistItem(n:number){return<li>{n}</li>;}<ul>{[1,2].map(listItem)}</ul>
Want a helper component? Create a function that implements CustomElementHandler and you can call it like an HTML element.
import{Attributes,CustomElementHandler}from"typed-html"functionButton(attributes:Attributes|undefined,contents:string[]){return<div><buttontype="button"class="original-class"{...attributes}>{contents}</button></div>;}// OrconstButton:CustomElementHandler=(attributes,contents)=><div><buttontype="button"class="original-class"{...attributes}>{contents}</button></div>;}console.log(<Buttonstyle="color:#f00">ButtonText</Button>);
Prints:
<div><buttontype="button"class="original-class"style="color:#f00">Button Text</button></div>
It's possible to write React-style components as well. Consider the example below.
import{Attributes,CustomElementHandler}from"typed-html"functionButton({ children, ...attributes}:Attributes){return<div><buttontype="button"class="original-class"{...attributes}>{children}</button></div>;}console.log(<Buttonstyle="color:#f00">ButtonText</Button>);
Prints:
<div><buttontype="button"class="original-class"style="color:#f00">Button Text</button></div>
Security isNOT a feature. This library doesNOT sanitize.
constscript='<script>alert("hacked!")</script>';constbody=<body>{script}</body>;
Will result in:
<body><script>alert('hacked!');</script></body>
If you need sanitization, you can use something likesanitize-html.
All HTML elements and attributes are supported, except for thesvg.
- Supported html elements:https://dev.w3.org/html5/html-author/#the-elements
- Supported html events:http://htmlcss.wikia.com/wiki/HTML5_Event_Attributes
Missing an element or attribute? Please create an issue or a PR to add it. It's easy to add.
Void elements (elements without closing tags) are supported, however you should close them in TypeScript.
constimg=<imghref="/foo/bar.png">;// => Error! JSX element 'img' has no corresponding closing tag.
In the example above, closing the image tag is required for valid TSX code:
constimg=<imghref="/foo/bar.png"></img>;// => '<img href="/foo/bar.png">'
Seethis code for a list of supported void elements.
All HTML attributes support a string value, however some attributes also support anumber
,Date
orboolean
(or absent value) type:
<metervalue={1}min={0}max={5}low={1}high={4}optimum={3}></meter>;// => <meter value="1" min="0" max="5" low="1" high="4" optimum="3"></meter><olstart={3}></ol>;<progressvalue={3}max={4}></progress>;<tdcolspan={3}rowspan={3}></td>;<thcolspan={3}rowspan={3}></th>;constdate=newDate('1914-12-20T08:00');<timedatetime={date}></time>;// => <time datetime="1914-12-20T08:00:00.000Z"></time><insdatetime={date}>updated</ins>;<deldatetime={date}>old</del>;// => <form> <input type="checkbox" checked> </form><formnovalidate={false}><inputtype="checkbox"checkeddisabled={false}></input></form>
You can add custom elements by adding them to theintrinsic elements yourself:
// MyCustomElements.d.tsdeclarenamespaceJSX{interfaceCustomElement{customAttribute?:string;}interfaceIntrinsicElements{myCustomElement:CustomElement;}}
Now you can use it:
// UseCustomElement.tsimport*aselementsfrom'typed-html';constmyElement=<myCustomElementcustomAttribute="customValue"></myCustomElement>console.log(myElement);
This prints:
<my-custom-elementcustom-attribute="customValue"></my-custom-element>
Custom attribute names are already supported out-of-the-box for attributes with a dash (-
) in the name. For example:
<buttondata-menu-item="3"></button>
As a browser is case insensitive when it comes to element and attribute names, it is common practice to usekebab case for this. However<custom-element></custom-element>
is not allowed in TypeScript. Thereforetyped-html
will transform<customElement></customElement>
to<custom-element></custom-element>
.
This transformation also works for custom attributes you define on a custom element yourself. For example:
<customElementaCustomAttr="value"></customElement>
Becomes
<custom-elementa-custom-attr="value"></custom-element>
The way this works is by using TypeScript's jsx support, but not for jsx/react interoperability. Instead, it defines thenormal html tags asIntrinsicElements
in the JSX namespace.
At runtime, theelements.createElement
function is called for every html tag. It simply converts the given element to a string with minimal overhead.
This:
<olstart={2}>{[1,2].map(i=><li>{i}</li>)}</ol>
Compiles to:
elements.createElement("ol",{start:2},[1,2].map(function(li){returnelements.createElement("li",null,li);}));
Which translates to:
<olstart="2"><li>1</li><li>2</li></ol>
About
TypeSafe HTML templates using TypeScript. No need to learn a template library.