NEWS: Welcome to my new homepage! <3

feat: Implemented component syntax - figure - Unnamed repository; edit this file 'description' to name the repository.

figure

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit 3e73dc1735bddaf786cb3101e5a1778d4752a964
parent b38eec5eb647373a391c87bdc426fddf2c49441c
Author: typable <contact@typable.dev>
Date:   Fri,  7 Apr 2023 01:46:52 +0200

feat: Implemented component syntax

Diffstat:
MREADME.md | 38+++++++++++++++++++++++++++++---------
Mlib.ts | 74+++++++++++++++++++++++++++++++++++++++-----------------------------------
Mtypes.ts | 8++++++--
3 files changed, 74 insertions(+), 46 deletions(-)

diff --git a/README.md b/README.md @@ -6,20 +6,40 @@ Reactive template literals for React ```javascript import figure from '...'; -const { html, dyn } = figure({ createElement }); +// initialize figure +const { dist } = figure({ createElement }); -const global = createContext({}); +// add your components to a bundle +const ElementBundle = { + button: Button, +}; -function App() { - const [name, setName] = useState('world'); +// add your bundles to the dictionary +const html = dist({ + el: ElementBundle, +}); - const context = {}; +function App() { + return html` + <main> + <el:button + type="primary" + on:click=${() => console.log('It works!')} + > + <span>Click me</span> + </el:button> + </main> + `; +} +function Button() { return html` - ${dyn(global.Provider, { value: context }), html` - <h1>Hello ${name}!</h1> - <p>Some description text.</p> - `} + <button + on:click=${props?.onClick} + class="btn btn--${props?.type}" + > + ${props?.children} + </button> `; } diff --git a/lib.ts b/lib.ts @@ -1,4 +1,4 @@ -import { Options, Props, ReactElement, ReactFunction, Refs, Slices, Values } from './types.ts'; +import { Options, Props, ReactElement, ReactFunction, Refs, Slices, Values, Dict } from './types.ts'; export default function figure({ createElement }: Options) { @@ -8,24 +8,34 @@ export default function figure({ createElement }: Options) { let count = 0; /** - * Converts the template literal HTML syntax into React elements. - * @param {Slices} slices - The template literal slices - * @param {Values} values - The template literal values - * @return {ReactElement[]} The converted HTML as React elements. + * Returns the a function for rendering HTML. + * @param {Dict} dict - The dictionary for resolving React components. + * @return {Function} The function for rendering HTML. */ - function html(slices: Slices, ...values: Values): ReactElement[] { - const [html, refs] = compose(slices, values); - let dom; - try { - dom = parser.parseFromString(html, 'text/html'); - } - catch (error) { - console.error(error); - throw 'Invalid DOM structure!'; + function dict(dict?: Dict): (slices: Slices, ...values: Values) => ReactElement[] { + + /** + * Converts the template literal HTML syntax into React elements. + * @param {Slices} slices - The template literal slices + * @param {Values} values - The template literal values + * @return {ReactElement[]} The converted HTML as React elements. + */ + function html(slices: Slices, ...values: Values): ReactElement[] { + const [html, refs] = compose(slices, values); + let dom; + try { + dom = parser.parseFromString(html, 'text/html'); + } + catch (error) { + console.error(error); + throw 'Invalid DOM structure!'; + } + // collect all nodes from head and body + const nodes = [...dom.head.childNodes, ...dom.body.childNodes]; + return nodes.map((node) => render(node, refs, dict ?? {})); } - // collect all nodes from head and body - const nodes = [...dom.head.childNodes, ...dom.body.childNodes]; - return nodes.map((node) => render(node, refs)); + + return html; } /** @@ -89,7 +99,7 @@ export default function figure({ createElement }: Options) { * @param {Refs} refs - The values mapped to there references * @return {ReactElement[]} The converted HTML node as React element */ - function render(node: Node, refs: Refs): ReactElement[] { + function render(node: Node, refs: Refs, dict: Dict): ReactElement[] { if (node.nodeType === Node.TEXT_NODE) { const text = node as Text; if (text.textContent == null) { @@ -103,7 +113,7 @@ export default function figure({ createElement }: Options) { return []; } const element = node as HTMLElement; - const tag = element.tagName; + const tag = element.tagName.toLowerCase(); const props: Props = {}; // iterate over each attribute and add it to the props for (const attribute of element.attributes) { @@ -157,22 +167,16 @@ export default function figure({ createElement }: Options) { } const children: ReactElement[] = []; // recursively render all child nodes - (node.childNodes ?? []).forEach((child) => children.push(...render(child, refs))); - return [createElement(tag, props, ...children)]; - } - - /** - * Creates a dynamic component with its own state. - * Should be used if the component is statefull (contains hooks). - * - * @param {ReactFunction} element - The string containing references - * @param {Props} props - The component properties - * @param {ReactElement[]} children - The child elements - * @return {ReactElement} The created React component - */ - function dyn(element: ReactFunction, props?: Props, children?: ReactElement[]): ReactElement { - return createElement(element, props, children); + (node.childNodes ?? []).forEach((child) => children.push(...render(child, refs, dict))); + const domain = tag.split(':'); + // look up tag name in dictionary + // deno-lint-ignore no-explicit-any + const component: ReactFunction | undefined = domain.reduce((dict: any, level) => { + return dict?.[level]; + }, dict); + // use React component or tag name + return [createElement(component ?? tag, props, ...children)]; } - return { html, dyn }; + return { dict }; } diff --git a/types.ts b/types.ts @@ -1,9 +1,13 @@ export type ReactElement = unknown; -export type ReactFunction = unknown; -export type CreateElement = (element: ReactFunction, props?: Props, ...children: ReactElement[]) => ReactElement; +export type ReactFunction = (props: unknown) => ReactElement; +export type CreateElement = (element: ReactFunction | string, props?: Props, ...children: ReactElement[]) => ReactElement; export type Options = { createElement: CreateElement }; export type Refs = Record<string, unknown>; export type Props = Record<string, unknown>; export type Slices = TemplateStringsArray; export type Values = unknown[]; + +export interface Dict { + [key: string]: ReactFunction | Dict; +}