From bdf0b3a98b3d8cc2768eb241bebe6e462a52eb1c Mon Sep 17 00:00:00 2001 From: Derek Hawker Date: Fri, 22 Dec 2017 10:34:11 +0100 Subject: [PATCH] feat(helper components): Add support for using functions as HTML elements --- readme.md | 25 +++++++++++++++++++++++++ src/elements.tsx | 32 +++++++++++++++++++++----------- test/html-fragments.spec.tsx | 12 ++++++++++++ 3 files changed, 58 insertions(+), 11 deletions(-) diff --git a/readme.md b/readme.md index a014049d..11457423 100644 --- a/readme.md +++ b/readme.md @@ -113,6 +113,31 @@ function listItem(n: number) { ``` +#### Using a helper template like an element + +Want a helper component? Create a function that implements CustomElementHandler and you can call it like an HTML element. + +```typescript +import {Attributes, CustomElementHandler} from "typed-html" + +function Button(attributes: Attributes | undefined, contents: string[]) { + return
; +} +// Or +const Button: CustomElementHandler = (attributes, contents) =>
; +} + +console.log(); +``` + +Prints: + +```html +
+ +
+``` + ## Supported HTML All HTML elements and attributes are supported, except for the [svg](https://www.w3.org/TR/SVG/). diff --git a/src/elements.tsx b/src/elements.tsx index f665eb2a..61762e61 100644 --- a/src/elements.tsx +++ b/src/elements.tsx @@ -4,6 +4,16 @@ import * as os from 'os'; +type AttributeValue = number | string | Date | boolean; + +export interface CustomElementHandler { + (attributes: Attributes | undefined, contents: string[]): string +} + +export interface Attributes { + [key: string]: AttributeValue; +} + const capitalACharCode = 'A'.charCodeAt(0); const capitalZCharCode = 'Z'.charCodeAt(0); @@ -12,12 +22,6 @@ const isUpper = (input: string, index: number) => { return capitalACharCode <= charCode && capitalZCharCode >= charCode; }; -type AttributeValue = number | string | Date | boolean; - -interface Attributes { - [key: string]: AttributeValue; -} - const toKebabCase = (camelCased: string) => { let kebabCased = ''; for (let i = 0; i < camelCased.length; i++) { @@ -103,11 +107,17 @@ const isVoidElement = (tagName: string) => { ].indexOf(tagName) > -1; }; -export function createElement(name: string, attributes: Attributes | undefined, ...contents: string[]) { - const tagName = toKebabCase(name); - if (isVoidElement(tagName) && !contents.length) { - return `<${tagName}${attributesToString(attributes)}>`; +export function createElement(name: string | CustomElementHandler, + attributes: Attributes | undefined, + ...contents: string[]) { + if (typeof name === 'function') { + return name(attributes, contents); } else { - return `<${tagName}${attributesToString(attributes)}>${contentsToString(contents)}`; + const tagName = toKebabCase(name); + if (isVoidElement(tagName) && !contents.length) { + return `<${tagName}${attributesToString(attributes)}>`; + } else { + return `<${tagName}${attributesToString(attributes)}>${contentsToString(contents)}`; + } } } diff --git a/test/html-fragments.spec.tsx b/test/html-fragments.spec.tsx index d1c43f6b..e82f8ec4 100644 --- a/test/html-fragments.spec.tsx +++ b/test/html-fragments.spec.tsx @@ -79,4 +79,16 @@ describe('using a number attribute', () => { describe('custom elements', () => { testEqual('', () => ); testEqual('
', () =>
); +}); + +describe('helper components', () => { + const Header: elements.CustomElementHandler = (attributes, contents) =>

{contents}

; + + function Button(attributes: elements.Attributes | undefined, contents: string[]) { + return ; + } + + testEqual('

Header Text

', () =>
Header Text
); + testEqual('', () => ', () => ); }); \ No newline at end of file