All Projects → michael-klein → Funcy.js

michael-klein / Funcy.js

Licence: mit
funcy.js - a functional web components wrapper

Programming Languages

javascript
184084 projects - #8 most used programming language

Projects that are alternatives of or similar to Funcy.js

vaadin-checkbox
The Web Component for customized checkboxes. Part of the Vaadin components.
Stars: ✭ 18 (-79.31%)
Mutual labels:  webcomponents, web-components
Switzerland
🇨🇭Switzerland takes a functional approach to Web Components by applying middleware to your components. Supports Redux, attribute mutations, CSS variables, React-esque setState/state, etc… out-of-the-box, along with Shadow DOM for style encapsulation and Custom Elements for interoperability.
Stars: ✭ 261 (+200%)
Mutual labels:  functional, webcomponents
cwco
Powerful and Fast Web Component Library with a Simple API
Stars: ✭ 27 (-68.97%)
Mutual labels:  webcomponents, web-components
web-component
Lightweight library providing interface for building web components
Stars: ✭ 37 (-57.47%)
Mutual labels:  webcomponents, web-components
Awesome Stenciljs
List of Awesome Web Components Built with StencilJS
Stars: ✭ 520 (+497.7%)
Mutual labels:  web-components, webcomponents
toggle-icon
toggle-icon is a custom element created with Polymer. It provides an extremely powerful and customizable switch that looks like a paper-icon-button.
Stars: ✭ 21 (-75.86%)
Mutual labels:  webcomponents, web-components
anywhere-webcomponents
A UI work in progress based on custom elements (web components) for use in anywhere.
Stars: ✭ 17 (-80.46%)
Mutual labels:  webcomponents, web-components
scheduler-component
A Web Component wrapper for FullCalendar library that uses Polymer version 2.0 and ES6.
Stars: ✭ 24 (-72.41%)
Mutual labels:  webcomponents, web-components
Vanilla Colorful
A tiny color picker custom element for modern web apps (2.7 KB) 🎨
Stars: ✭ 467 (+436.78%)
Mutual labels:  web-components, webcomponents
Vaadin
An evolving set of open source web components for building mobile and desktop web applications in modern browsers.
Stars: ✭ 424 (+387.36%)
Mutual labels:  web-components, webcomponents
Wired Elements
Collection of custom elements that appear hand drawn. Great for wireframes or a fun look.
Stars: ✭ 8,848 (+10070.11%)
Mutual labels:  web-components, webcomponents
Blazor.fast
A tiny wrapper around Fast and Fluent Web Components to integrate with Blazor and easily use the EditForm components
Stars: ✭ 23 (-73.56%)
Mutual labels:  web-components, webcomponents
lit-components
Moved to https://github.com/vaadin/component-mixins
Stars: ✭ 59 (-32.18%)
Mutual labels:  webcomponents, web-components
vaadin-context-menu
The responsive Web Component for showing context dependent items for any element on the page. Part of the Vaadin components.
Stars: ✭ 26 (-70.11%)
Mutual labels:  webcomponents, web-components
lego
🚀 Web-components made lightweight & Future-Proof.
Stars: ✭ 69 (-20.69%)
Mutual labels:  webcomponents, web-components
custom-elements-manifest
Custom Elements Manifest is a file format that describes custom elements in your project.
Stars: ✭ 81 (-6.9%)
Mutual labels:  webcomponents, web-components
symbiote.js
Simple, light and very powerful library to create embedded components for any purpose, with a data flow management included.
Stars: ✭ 40 (-54.02%)
Mutual labels:  webcomponents, web-components
modulor-html
Missing template engine for Web Components
Stars: ✭ 36 (-58.62%)
Mutual labels:  webcomponents, web-components
Vaadin Core
An evolving set of free, open source web components for building mobile and desktop web applications in modern browsers.
Stars: ✭ 382 (+339.08%)
Mutual labels:  web-components, webcomponents
Elix
High-quality, customizable web components for common user interface patterns
Stars: ✭ 546 (+527.59%)
Mutual labels:  web-components, webcomponents

funcyjs logo

A functional web-components wrapper

example of code

Why 'funcy.js'?

Naming things is hard and 'funcyjs' (pronounced like funky) seemed like a fun, recognizable name.

What is it?

funcy.js seeks to provide a functional way of defining web components, very much akin to react functional components with hooks.

the hooks functionality in funcyjs is powered by: hookuspocus

Here's a simple TODO app implemented with funcyjs: https://codepen.io/michael-klein/pen/xmQZBx

Browser Compatibility

The library is published is not transpiled for browser compatibility and does not contain any polyfills. As such you can use it as an es6 module in the latest version of chrome and other browsers that implement the latest JavaScript features including web components v1, but it will fail horribly anywhere else, so you will have to provide polyfills/transpilation if you want to use this in more browsers.

Installation

Using npm:

npm install funcy-components
import {defineComponent} from "funcy-components"

As ES6 module via hotlinking from unpkg:

import {defineComponent} from "https://unpkg.com/funcy-components/dist/core.min.mjs"

or the full version with all hooks:

import {defineComponent} from "https://unpkg.com/funcy-components/dist/full.min.mjs"

Usage

The bare minimum:

import {defineComponent} from "https://unpkg.com/funcy-components/dist/full.min.mjs";

defineComponent("a-component", () => {
 const div = document.createElement("div");
 div.innerHTML = "Hello World!";
 return div;
});

✏️pen

What's happening here? defineComponent is a method with the signature:

function defineComponent(name:string, component:(props:any) => View, options:DefineComponentOptions = {}):void;

It will define a web component via customElements.define by internally creating a class that extends HTMLElement using the supplied name.

component is a function that accepts props and returns a View (just like functional components in react). It will be called whenever the component needs to (re-)render. A View is anything that can be consumed by a renderer (more on that in a bit). In the above example, the View is simply a div element. The default renderer will simply replace the current content of the shadowRoot with the view (unless you return the same nodes).

defineComponent also accepts an options object, that allows you to define observed attributes and pass options to attachShadow

interface DefineComponentOptions {
  observedAttributes:string[],
  shadowOptions:ShadowRootInit
}

Props

Normally, you can only pass data to custom elements via attributes, which only support string values. funcyjs enables you to pass prps between funcyjs components using thr prps method like this:

defineComponent(
  "prop-sender",
  () => {
    const html = usePreactHtm();
    return html`
      <prop-receiver ...${prps({greeting: "hello World"})}></prop-receiver>
    `;
  }
);
defineComponent(
  "prop-receiver",
  (props) => {
    const html = usePreactHtm();
    return html`
      <div>
        ${props.greeting}
      </div>
    `;
  }
);

✏️pen

prps will actually return an object like this:

{
  "data-props": propsId
}

You can spread it on the component with htm or just set a "data-props" attribute manually if you just use DOM. Internally, funcyjs listens to changes to the data-props argument and qeueus re-renders if the passed props change.

Hooks: The basics

Hooks are a way to use state or other internal features in your functional components. They were first popularized by react. Read more about the motivation and use of the basic hooks (useReducer, useState, useEffect) in the react docs: https://reactjs.org/docs/hooks-intro.html. The basic hooks that funcyjs has in common with react should work exactly the same. If they don't, pease submit an issue :)

In the following I will explain how to use some of the hooks which are specific to funcyjs.

Custom renderers

A custom renderer is a function that takes a View and a shadowRoot and knows how to render the View to the shadowRoot. It is called after a component renders with the generated View and the shadowRoot of the elment. For example, this is the default renderer:

export const defaultRenderer = (view, shadowRoot) => {
  if (
    !(view instanceof NodeList
      ? shadowRoot.contains(view[0])
      : shadowRoot.contains(view))
  ) {
    shadowRoot.innerHTML = "";
    if (view instanceof NodeList) {
      view.forEach(node => shadowRoot.appendChild(node));
    } else {
      shadowRoot.appendChild(view);
    }
  }
};

You can define your own custom renderer with the useRenderer hook. funcyjs exports a custom usePreactHtm hook that uses htm and preact in the full bundle:

import { createHook, useRenderer } from "../export_core.mjs";
import { html, render } from "../../node_modules/htm/preact/standalone.mjs";
export const usePreactHtm = createHook(() => {
  useRenderer((view, shadowRoot) => {
    render(view, shadowRoot);
  });
  return html;
});

It also returns a html template tag that can be used to construct the view which is consumed with the render call.

Attributes

CustomElements can have attributes, just like any other element. The useAttribute hook will enable you to access and modify these:

defineComponent(
  "attribute-example",
  () => {
    const html = usePreactHtm();
    const [name, setName] = useAttribute("name");
    return html`
        <input type="text" onInput=${e => setName(e.target.value)} value=${name}></input>
    `;
  },
  {
    observedAttributes: ["name"]
  }
);

✏️pen

The above example will reflect changes you make to the input back to the attribute on the component in the DOM. Note that we also supplied "name" as an observedAttribute, so that when an outside source changes the attribute, the component will re-render (the setter from useAttribute won't trigger a re-render).

CSS

You can render CSS directly to the view, if you which. You can also use the useCSS hook for that purpose. The hook can act as a normal function or a template tag and will render the CSS you pass it to the shadowRoot:

defineComponent(
  "css-example",
  () => {
    const html = usePreactHtm();
    useCSS('h1 {color:green;}');
    const css = useCSS;
    css`
      h2 {
        color:red;
      }
      `
    return html`
      <div>
        <h1>green</h1>
        <h2>red</h2>
      </div>
    `;
  }
);

✏️pen

Exposing an API

CustomElements can expose API methods for others to consume. In funcyjs, this is done through the useExposeMethod hook:

defineComponent(
  "expose-method",
  () => {
    const html = usePreactHtm();
    useExposeMethod("methodName", () => alert("you used this method!"));
    return html`<div>something</div>`;
  }
);

Note that wether you use an arrow function or a normal function, this will never be bound to the CustomElement instance.

others:

You can access the host element, the shadow root or get information on the connected state of the component with the useHostElement, useShadowRoot useConnectedState hooks. Use them sparingly if at all.

currently implemented hooks:

core hooks:

  • useReducer
  • useState
  • useEffect
  • useRenderer
  • useAttribute
  • useCSS
  • useExposeMethod
  • useConnectedState
  • useHostElement
  • useShadowRoot

other (only present in full bundles):

  • usePreactHtm

License

MIT License

Copyright (c) 2019 Michael Klein

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].