2.9.2 • Published 14 days ago

@htmlplus/element v2.9.2

Weekly downloads
-
License
MIT
Repository
github
Last release
14 days ago

Create Custom HTML Element

A powerful tool for building a scalable, reusable, fast, and lightweight UI Component Library for any web technologies, powered by Custom Elements.

Table Of Content

Features

  • Plugin-Based: Facilitates the seamless development of diverse plugins and the customization of outputs to meet specific requirements
  • Built-In Plugins: Provides a variety of plugins that cater to different requirements.
  • Global Config: Provides the ability to define global configs for all elements.
  • Typings: Creates TypeScript types for seamless element usage across different environments.
  • TypeScript + JSX: Using two powerful tools, TypeScript and JSX, to create elements.
  • Built-In Utilities: Provides a set of JavaScript utility functions used across multiple elements.
  • Secure: Restricts unwanted access to internal properties and methods.
  • Style File Recognition: Identifies and links the relevant style file to the element.
  • Tag Name Recognition: Generates tag name from the class name.
  • Clean Syntax: Uses a minimal amount of code to achieve the same functionality, making the code easier to read, understand, and maintain.
  • Attribute Over Class: Uses HTML attributes instead of HTML class names to keep the size of the start tag short, making it more readable and preventing the DOM from getting dirty.

Quick Start

Before proceeding, ensure you have the latest LTS version of Node.js installed on your system.

1- Create a new project

npm init @htmlplus/element@latest

2- Navigate to the project directory

cd htmlplus-project

3- Install the dependencies

npm i

4- Start the project

npm start

First Element

An example demonstrating the implementation and usage of an element.

Each element is stored in a file such as my-counter.tsx.

import { Element, State } from '@htmlplus/element';

@Element()
export class MyCounter {
  @State()
  value: number = 0;

  render() {
    return (
      <host onClick={() => this.value++}>
        Count is {this.value}
      </host>
    )
  }
}

The element's style is stored in a file such as my-counter.css, which shares the same name as the element file my-counter.tsx.

:host {
  display: inline-block;
  border: 1px solid black;
  color: black;
  padding: 1em;
  cursor: pointer;
}

To execute the element, include it in the index.html file.

<body>
  <my-counter></my-counter>
</body>

Decorators

Decorators can greatly enhance code maintainability, improving efficiency, readability, and reusability.

Used to bind a method of a class to the current context, making it easier to reference this within the method.

In the my-counter.tsx file.

import { Bind, Element, State } from '@htmlplus/element';

@Element()
export class MyCounter {
  @State()
  value: number = 0;

  @Bind()
  onClick() {
    this.value++;
  }

  render() {
    return (
      <host onClick={this.onClick}>
        Count is {this.value}
      </host>
    )
  }
}

In the index.html file.

<my-counter></my-counter>

In the my-element.tsx file.

import { Direction, Element } from '@htmlplus/element';

@Element()
export class MyElement {
  @Direction()
  direction!: 'ltr' | 'rtl';

  render()  {
    return (
      <div>
        The direction of the element is
        <u>
          {this.direction}
        </u>
      </div>
    )
  }
}

In the index.html file.

<body dir="rtl">
  <my-element></my-element>
</body>

The class marked with this decorator is considered a Custom Element, and its name, in kebab-case, serves as the element name.

It is important to note that each file can only contain one class with this condition.

In the say-hello.tsx file.

import { Element } from '@htmlplus/element';

@Element()
export class SayHello {
  render() {
    return <div>Hello World</div>
  }
}

In the index.html file.

<say-hello></say-hello>

Parameters:

  • options (Optional) An object that configures options for the event dispatcher.
    • bubbles (Optional) A boolean value indicating whether the event bubbles. The default is false.
    • cancelable (Optional) A boolean value indicating whether the event can be cancelled. The default is false.
    • composed (Optional) A boolean value indicating whether the event will trigger listeners outside of a shadow root (see Event.composed for more details). The default is false.

In the my-button.tsx file.

import { Element, Event, EventEmitter } from '@htmlplus/element';

@Element()
export class MyButton {
  @Event()
  myClick!: EventEmitter<string>;

  render() {
    return (
      <button onClick={() => this.myClick("It's a message form MyButton!")}>
        <slot />
      </button>
    )
  }
}

In the index.html file.

<my-button id="button">Button</my-button>

<script>
  document
    .getElementById('button')
    .addEventListener('my-click', (event) => {
      alert(event.detail);
    });
</script>

Indicates the host of the element.

In the my-element.tsx file.

import { Element, Host } from '@htmlplus/element';

@Element()
export class MyElement {
  @Host()
  host!: HTMLElement;

  get isSame() {
    return this.host == document.querySelector('my-element');
  }

  connectedCallback() {
    console.log('Is Same: ' + this.isSame);
  }
}

In the index.html file.

<my-element></my-element>

Indicates whether the direction of the element is Right-To-Left or not.

In the my-element.tsx file.

import { Element, IsRTL } from '@htmlplus/element';

@Element()
export class MyElement {
  @IsRTL()
  isRTL!: boolean;

  render()  {
    return (
      <div>
        The direction of the element is
        <u>
          {this.isRTL ? 'rtl' : 'ltr'}
        </u>
      </div>
    )
  }
}

In the index.html file.

<body dir="rtl">
  <my-element></my-element>
</body>

Will be called whenever the specified event is delivered to the target More.

Parameters:

  • type (Required) A case-sensitive string representing the Event Type to listen for.
  • options (Optional) An object that configures options for the event listener.
    • capture (Optional) A boolean value indicating that events of this type will be dispatched to the registered listener before being dispatched to any EventTarget beneath it in the DOM tree. If not specified, defaults to false.
    • once (Optional) A boolean value indicating that the listener should be invoked at most once after being added. If true, the listener would be automatically removed when invoked. If not specified, defaults to false.
    • passive (Optional) A boolean value that, if true, indicates that the function specified by listener will never call preventDefault(). If a passive listener does call preventDefault(), the user agent will do nothing other than generate a console warning.
    • signal (Optional) An AbortSignal. The listener will be removed when the given AbortSignal object's abort() method is called. If not specified, no AbortSignal is associated with the listener.
    • target (Optional) The target element, defaults to host.

In the my-button.tsx file.

import { Element, Listen } from '@htmlplus/element';

@Element()
export class MyButton {
  @Listen('click')
  onClick(event) {
    alert('The my-button was clicked!');
  }

  render() {
    return <slot />
  }
}

In the index.html file.

<my-button>Click Me</my-button>

Provides a way to encapsulate functionality within an element and invoke it as needed, both internally and externally.

In the my-counter.tsx file.

import { Element, Method, State } from '@htmlplus/element';

@Element()
export class MyCounter {
  @State()
  value: number = 0;

  @Method()
  increase() {
    this.value++;
  }

  render() {
    return (
      <host>
        Count is {this.value}
      </host>
    )
  }
}

In the index.html file.

<my-counter id="counter"></my-counter>

<script>
  setInterval(() => {
    document.getElementById('counter').increase();
  }, 1000);
</script>

Creates a reactive property, reflecting a corresponding attribute value, and updates the element when the property is set.

Parameters:

  • options (Optional) The configuration for property decorator.
    • attribute (Optional) Specifies the name of the attribute related to the property.
    • reflect (Optional) Whether property value is reflected back to the associated attribute. default is false.
    • type (Optional) Specifies the property type and supports data types. If this value is not set, it will be set automatically during transforming.

In the say-greeting.tsx file.

import { Element, Property } from '@htmlplus/element';

@Element()
export class SayGreeting {
  @Property()
  name?: string = 'Simon';

  render() {
    return <div>Hi {this.name}</div>
  }
}

In the index.html file.

<say-greeting name="Jan"></say-greeting>

Selects the first element in the shadow dom that matches a specified CSS selector.

Parameters:

  • selectors (Required) A string containing one or more selectors to match. This string must be a valid CSS selector string; if it isn't, a SyntaxError exception is thrown. See Locating DOM elements using selectors for more about selectors and how to manage them.

In the my-button.tsx file.

import { Element, Query } from '@htmlplus/element';

@Element()
export class MyButton {
  @Query('.btn')
  buttonRef!: HTMLButtonElement;

  loadedCallback() {
    console.log(this.buttonRef); // <button class="btn"></button>
  }

  render() {
    return (
      <button class="btn">
        <slot />
      </button>
    )
  }
}

In the index.html file.

<my-button>
  Button
</my-button>

Selects all elements in the shadow dom that match a specified CSS selector.

Parameters:

  • selectors (Required) A string containing one or more selectors to match against. This string must be a valid CSS selector string; if it's not, a SyntaxError exception is thrown. See Locating DOM elements using selectors for more information about using selectors to identify elements. Multiple selectors may be specified by separating them using commas.

In the my-button.tsx file.

import { Element, QueryAll } from '@htmlplus/element';

@Element()
export class MyButton {
  @QueryAll('span')
  spanRefs!: NodeList;

  loadedCallback() {
    console.log(this.spanRefs); // [span, span]
  }

  render() {
    return (
      <button>
        <span> Suffix </span>
        <b>
          <slot />
        </b>
        <span> Prefix </span>
      </button>
    )
  }
}

In the index.html file.

<my-button>
  Button
</my-button>

In the my-element.tsx file.

import { Element, Slots } from '@htmlplus/element';

@Element()
export class MyElement {
  @Slots()
  slots;

  connectedCallback() {
    console.log(this.slots); // {header: true, default: true, footer: true}
  }

  render() {
    return (
      <host>
        <slot name="header"></slot>
        <slot></slot>
        <slot name="footer"></slot>
      </host>
    )
  }
}

In the index.html file.

<my-element>
  <div slot="header">HEADER</div>
  <div>BODY</div>
  <div slot="footer">FOOTER</div>
</my-element>

Applying this decorator to any class property will trigger the element to re-render upon the desired property changes.

In the my-button.tsx file.

import { Element, State } from '@htmlplus/element';

@Element()
export class MyButton {
  @State()
  active?: boolean;

  toggle() {
    this.active = !this.active;
  }

  render() {
    return (
      <button onClick={() => this.toggle()}>
        Click To Change The Status ({this.active ? 'On' : 'Off'})
      </button>
    )
  }
}

In the index.html file.

<my-button></my-button>

Parameters:

  • keys (Optional) Collection of @Property() and @State() names.
  • immediate (Optional) Triggers the callback immediately after initialization.

In the my-element.tsx file.

import { Element, Property, Watch } from '@htmlplus/element';

@Element()
export class MyElement {
  @Property()
  value?: string;

  @Watch('value')
  watcher(key, newValue, oldValue) {
    console.log(key, newValue, oldValue);
  }
}

In the index.html file.

<my-element id="element"></my-element>

<script>
  setInterval(() => {
    document.getElementById('element').value = new Date();
  }, 1000);
</script>

Utilities

Utilities are a versatile tool in element building projects, eliminating the need for rewriting.

Indicates whether the Direction of the element is Right-To-Left or Left-To-Right.

TODO

TODO

TODO

Determines whether the given input string is a valid CSS Color or not.

TODO

isCSSColor('red')                       // true
isCSSColor('#ff0000')                   // true
isCSSColor('#ff000080')                 // true
isCSSColor('rgb(255, 0, 0)')            // true
isCSSColor('rgba(255, 0, 0, 0.3)')      // true
isCSSColor('hsl(120, 100%, 50%)')       // true
isCSSColor('hsla(120, 100%, 50%, 0.3)') // true
isCSSColor('invalid color')             // false

Indicates whether the direction of the element is Right-To-Left or not.

TODO

Selects the first element in the shadow dom that matches a specified CSS selector.

TODO

TODO

Returns the slots name.

TODO

Converts a value to a unit.

TODO

JSX

TODO

TODO

TODO

Lifecycles

Elements encompass several lifecycle methods, each triggered at different stages in the element's life cycle, enabling developers to control the element's behavior and perform customized actions.

TODO

A lifecycle callback method that is called each time the element is added to the document.

import { Element } from '@htmlplus/element';

@Element()
export class MyElement {
  connectedCallback() {
    console.log('Element is connected!');
  }
}

TODO

import { Element } from '@htmlplus/element';

@Element()
export class MyElement {
  disconnectedCallback() {
    console.log('Element is disconnected!');
  }
}

TODO

import { Element } from '@htmlplus/element';

@Element()
export class MyElement {
  loadedCallback() {
    console.log('Element is loaded!');
  }
}

TODO

TODO

Bundlers

TODO

TODO

TODO

Transformer

TODO

TODO

import { TransformerPlugin, transformer } from '@htmlplus/element';
import {
  customElement,
  extract,
  parse,
  read,
  style,
  validate,
} from '@htmlplus/element/transformer/index.js';

const plugins = [
  read(),
  parse(),
  validate(),
  extract(),
  style(),
  customElement()
];

const { start, run, finish } = transformer(...plugins);

await start();

const context1 = await run('/my-avatar.tsx');
const context2 = await run('/my-button.tsx');
const context3 = await run('/my-switch.tsx');

await finish();

TODO

import {
  assets,
  copy,
  customElement,
  document,
  extract,
  parse,
  read,
  readme,
  style,
  validate,
  visualStudioCode,
  webTypes
} from '@htmlplus/element/transformer/index.js';
2.9.2

14 days ago

2.9.1

17 days ago

2.9.0

23 days ago

2.8.1

1 month ago

2.8.0

1 month ago

2.7.0

2 months ago

2.6.0

2 months ago

2.4.1

2 months ago

2.5.0

2 months ago

2.5.1

2 months ago

2.4.0

2 months ago

2.3.0

2 months ago

2.2.0

2 months ago

2.1.6

2 months ago

2.1.2

3 months ago

2.1.4

3 months ago

2.1.3

3 months ago

2.1.5

2 months ago

2.1.1

3 months ago

2.1.0

3 months ago

2.0.0

3 months ago

1.1.0

3 months ago

1.0.0

5 months ago

0.8.5

6 months ago

0.8.4

6 months ago

0.8.6

5 months ago

0.7.9

7 months ago

0.7.8

7 months ago

0.8.1

6 months ago

0.8.0

6 months ago

0.8.3

6 months ago

0.8.2

6 months ago

0.7.4

1 year ago

0.7.3

1 year ago

0.7.6

12 months ago

0.7.5

1 year ago

0.7.7

12 months ago

0.7.2

1 year ago

0.7.1

1 year ago

0.7.0

1 year ago

0.6.7

1 year ago

0.6.6

1 year ago

0.6.9

1 year ago

0.6.8

1 year ago

0.5.4

1 year ago

0.5.3

1 year ago

0.5.6

1 year ago

0.5.5

1 year ago

0.5.2

1 year ago

0.5.1

2 years ago

0.5.8

1 year ago

0.5.7

1 year ago

0.5.9

1 year ago

0.6.3

1 year ago

0.6.2

1 year ago

0.6.5

1 year ago

0.6.4

1 year ago

0.6.1

1 year ago

0.6.0

1 year ago

0.4.9

2 years ago

0.4.8

2 years ago

0.4.5

2 years ago

0.4.7

2 years ago

0.5.0

2 years ago

0.4.4

2 years ago

0.4.3

2 years ago

0.4.2

2 years ago

0.4.1

2 years ago

0.4.0

2 years ago

0.3.2

2 years ago

0.3.1

2 years ago

0.3.3

2 years ago

0.3.0

2 years ago

0.2.0

2 years ago

0.1.8

2 years ago

0.1.7

2 years ago

0.1.9

2 years ago

0.1.4

2 years ago

0.1.6

2 years ago

0.1.5

2 years ago

0.0.11

2 years ago

0.0.12

2 years ago

0.0.13

2 years ago

0.0.14

2 years ago

0.1.0

2 years ago

0.1.2

2 years ago

0.1.1

2 years ago

0.0.15

2 years ago

0.0.16

2 years ago

0.0.17

2 years ago

0.0.18

2 years ago

0.1.3

2 years ago

0.0.10

2 years ago

0.0.9

2 years ago

0.0.8

2 years ago

0.0.7

2 years ago

0.0.6

2 years ago

0.0.5

2 years ago

0.0.4

2 years ago

0.0.3

2 years ago

0.0.2

2 years ago

0.0.1

2 years ago