@tybalt/core

Tybalt exports a set of utilities for making authoring web components easier. These are grouped together as @tybalt/core. A simple example of how to use this might be

defineComponent({
    name: 'flip-a-coin',
    shadowMode: 'closed',
    setup(): { return { rand: Math.random() } },
    render({ rand }) {
        return html`
            <t-if condition="${rand >= .5}">
                <p slot="true">Heads</p>
                <p slot="false">Tails</p>
            </t-if>
        `;
    },
});

In this example, we create a new web component, flip-a-coin, that can be used as follows

<flip-a-coin></flip-a-coin>

And will render, based on the value of a random number, one of the following two variants

<flip-a-coin>
    # Shadow Root
    <p>Heads</p>
</flip-a-coin>
<flip-a-coin>
    # Shadow Root
    <p>Tails</p>
</flip-a-coin>

To see the full specification for how to define a component, see the api section below.

Installation

Bundlers

You can install the package from npm. For example, using the npm cli

npm install --save @tybalt/core

Alternatively, you can install using pnpm or yarn.

Once you've installed the package you'll need to include it in your bundle. See the documentation from your bundler (webpack, swc, browserify, etc.) for details.

CDN

You can include Tybalt on your page directly by fetching it from a CDN. For example, using unpkg

import { defineComponent, html } from 'https://unpkg.com/@tybalt/core@0.0.10/dist/mjs/index.js';

Alternatively, you can use cdnjs, or jsDelivr.

API

defineComponent

defineComponent creates a new web component.

name

The name of the web component. This is used when creating a new instance of your web component. For instance, if you create a new component with the name my-component

defineComponent({
    name: 'my-component',
});

you can use it in your html with the same name

<my-component />

Note that component names have to contain the - character.

emits

emits is an array of strings, which are the CustomEvent types of all of the events this component might emit. This is used for static analysis purposes; if you emit an event that is not listed here or if you don't list an event here that you later emit it's totally fine.

defineComponent({
    name: 'emits-example',
    emits: ['my-event'],
});

props

props is an object that determines how attributes are handled for your web component.

import { required, string } from '@tybalt/validator';

defineComponent({
    name: 'props-example',
    props: {
        example: {
            default: 'foo',
            validator: compose(required(), string()),
        },
    },
});

setup

setup is a method that is called once a web component is connected to the DOM (it is called as part of connectedCallback, once all of the props has been supplied).

defineComponent({
    name: 'setup-script',
    setup(props) {
        return {
            myValue: 'my-value',
        };
    },
});

setup functions take two arguments. The first argument are the props, as specified in the props key on the argument to defineComponent.

defineComponent({
    name: 'setup-props',
    props: {
        one: { default: 'one' },
        two: { default: 'two' },
    },
    setup({ one, two }) {
        console.log(`one ${one.value} two ${two.value}`);
    },
});

The second argument is the context. Currently, it has the emit function on it, which you can use to emit an event.

defineComponent({
    name: 'setup-context',
    setup({}, { emit }): {
        emit('mounted');
    }
});

connectedCallback

Called as part of the web component life cycle.

defineComponent({
    name: 'connected-callback',
    connectedCallback() {
        console.log('connected to the dom');
    },
});

disconnectedCallback

Called as part of the web component life cycle.

defineComponent({
    name: 'disconnected-callback',
    disconnectedCallback() {
        console.log('disconnected from the dom');
    },
});

adoptedCallback

Called as part of the web component life cycle.

defineComponent({
    name: 'adopted-callback',
    adoptedCallback() {
        console.log('adopted to the dom');
    },
});

shadowMode

The mode that the shadow root mode the dom should be attached in. One of: open, closed.

defineComponent({
    name: 'shadow-mode',
    shadowMode: 'open',
});

createContext

This method is used to create a context for a web component or set of web components. This is a solution for how to share state between many web components, for example, when implementing theming.

To create a new context, call createContext

import { createContext } from '@tybalt/context';

export const theme = createContext('theme', {
    primaryColor: 'rebeccapurple',
    secondaryColor: 'bisque',
    fontFamily: 'Consolas',
    linkColor: '#ffcc99',
});

The import it into your component, and pass it to contexts

import { theme } from '../contexts';

export default defineComponent({
    name: 'example-link',
    props: { href: {} },
    render({ href, theme }) {
        return html`
            <style>
                .a {
                    color: ${theme.linkColor};
                }
            </style>
            <a href="${href}"><slot></slot></a>
        `;
    },
    contexts: { theme },
});

Now, every time there is a new theme context (say when switching from a light theme to a dark theme), we'll call render with the new theme.

render

Used for rendering an html template, as produced by the html tagged template literal, and renders it into a containing element. For instance, calling the following code

const wrapper = document.createElement('div');
const template = html`<input type="text" @change="${listener}"></input>`;

render(template, wrapper);

Would produce an element with the following markup

<div>
    <input type="text"></input>
</div>

With a listener for the change event attached to the input.

Components

Tybalt exports a set of components for use for common templating tasks. These components are registered as soon as you require the tybalt core library; there is no extra work needed to set them up.

t-if

Allows you to render one template if a condition is true, and another if that condition is false.

In the following example, on December 25th the component will render It's Christmas!, but on other days it will render It's not Christmas.

defineComponent({
    name: 'is-it-christmas',
    render() {
        const today = new Date();
        const isChristmas = today.getMonth() === 11 && today.getDate() === '25';
        return html`
            <t-if condition="${isChristmas}">
                <span slot="true">It's Christmas!</span>
                <span slot="false">It's not Christmas.</span>
            </t-if>
        `;
    },
});

t-switch

Allows you to render a single template out of many that corresponds to a value.

In the following example, when getCity returns Seattle, it will recommend you visit the Space Needle; when getCity returns Moscow, it will recommend you visit Red Square; and when getCity returns Madrid, it will recommend you visit the Reina Sofia.

defineComponent({
    name: 'city-switcher',
    render() {
        const city = getCity();
        return html`
            <t-switch condition="${city}">
                <div slot="Seattle">You're in Seattle; go see the Space Needle!</div>
                <div slot="Moscow">You're in Moscow; go see Krasnii Ploshad!</div>
                <div slot="Madrid">You're in Madrid; go to the Reina Sofia</div>
            </t-switch>
        `;
    },
});