# P-Element

## CustomElement base class

### Static properties

#### `projectorMode` "append", "merge" or "replace"

The projector mode when using `templateFromString` function

#### `style` string

The style is adopted to the rootNode.
if this is set there is no need for calling `templateFromString`. A root node is created and replaced with the result of the `render` function.

example

``` typescript
class MyElement extends CustomElement {
  static style = `
  :host {
    color: red;
  }`;

  render = () :VNode {
    return <div>Hello world</div>;
  }
}
```

#### `isFormAssociated` boolean

If true, elementInternals are created using attachInternals in the constructor.

example

``` typescript
class MyElement extends CustomElement {

  static isFormAssociated = true;

  @property({type: "string", attribute: "value", reflect: true })
  value: string = "Peter" ;

  updated(property: string, oldValue: any, newValue: any) {
    if (property === "value"){
      this.internals.setFormValue(this.value);
    }
  }

  render = () :VNode {
    return <div>Hello {this.value}.</div>;
  }
}
```

### Decorators

#### `@CustomElementConfig`

Use this decorator to define the custom element

example

``` typescript
@CustomElementConfig({
  tagName: "p-foo",
})
class PFooElement extends CustomElement {
  ...
}
```

If you want to extend a build in component you need to specify the buildin.

example

``` typescript
@CustomElementConfig({
    tagName: "super-a",
    options: {
      extends: "a",
    },
  })
class SuperAnchorElement extends HTMLAnchorElement {
  ...
}
```

#### `@property`

Use the `@property` decorator to define reactive properties.

example

``` typescript
@CustomElementConfig({
  tagName: "p-foo",
})
class PFooElement extends CustomElement {
  ...

  @property({type: "string", attribute: "nick-name", reflect: true})
  nickName: string;

  attributeChangedCallback(name: string, oldValue: string, newValue: string): void {
    super.attributeChangedCallback(name, oldValue, newValue);
    ...
  }
```
The `type` option could be  "string", "number", "boolean" or "object".

If `attribute` option is set the property will be set on attribute change. If you implement a `attributeChangeCallback` you need to call the `super.attributeChangedCallback`.

If `reflect` is `true` the attribute is set on property change.

Use `converter` option to convert attribute to property

``` typescript
const stringArrayConverter: AttributeConverter<string[]> = {
  fromAttribute: (value) => {
    if (!value) {
      return null;
    }
    return value.split(",").map((v) => v.trim());
  },
  toAttribute: (value) => {
    if (!value.join) {
      return null;
    }
    return value.join(",");
  },
};

class MyElement extend CustomElement {
  @property({attribute: "items", reflect: true, converter: stringArrayConverter})
  items: string[] = ["foo", "bar"];
}
```
#### `@query` decorator

Use the `@query` decorator to define a property that returns an element from the shadow dom.

example

``` typescript
class PFooElement extends CustomElement {
  ...
  @query("#MyInput")
  nameInput: HTMLInputElement;

  render = () => {
    return <div>
      <label for="MyInput"></label>
      <input type="text" id="MyInput" />
    </div>;
  }
}
```

#### `@propertyRenderOnSet` `@renderOnSet`

Properties decorated with `@propertyRenderOnSet` or `@renderOnSet` call renderNow when setting a value.

example

``` typescript
class PFooElement extends CustomElement {
  ...
  @propertyRenderOnSet
  public foo: string = "foo";
}
```

#### `@bind`
(deprecated, use arrow function `myFn = () => console.log('myFn')`)

Functions decorated with `@bind` will be replaced with the result of bind(this) on the function.

## CustomElementController

The `CustomElementController` is an abstract base class that allows you to create reusable controllers that can be attached to custom elements. Controllers have access to the host element's lifecycle and can trigger renders.

### Creating a Controller

Controllers must extend the `CustomElementController` class and can implement the `ICustomElementController` interface methods:

``` typescript
import { CustomElementController, CustomElement } from 'p-elements-core';

class MyController extends CustomElementController {
  // Called when the controller is initialized
  init?(): void {
    console.log('Controller initialized');
  }

  // Called when the host element connects to the DOM
  connected?(): void {
    console.log('Host connected');
  }

  // Called when the host element disconnects from the DOM
  disconnected?(): void {
    console.log('Host disconnected');
  }

  // Called before the host element renders
  hostRenderStart?(): void {
    console.log('Host render starting');
  }

  // Called after the host element finishes rendering
  hostRenderDone?(): void {
    console.log('Host render done');
  }
}
```

### Using a Controller

Create an instance of your controller in the custom element constructor or init method, passing the host element:

``` typescript
@CustomElementConfig({
  tagName: "my-element",
})
class MyElement extends CustomElement {
  private myController: MyController;

  init() {
    this.myController = new MyController(this);
  }

  render = () => {
    return <div>Element with controller</div>;
  }
}
```

The controller is automatically registered with the host element when constructed.

### Controller Methods

Controllers have access to the following methods:

- `renderNow()`: Triggers an immediate render of the host element
- `scheduleRender()`: Schedules a render of the host element
- `hostElement`: Property that returns the host custom element

### Example: Timer Controller

``` typescript
class TimerController extends CustomElementController {
  private intervalId?: number;

  connected() {
    this.intervalId = window.setInterval(() => {
      console.log('Timer tick');
      this.scheduleRender(); // Update the host element
    }, 1000);
  }

  disconnected() {
    if (this.intervalId) {
      clearInterval(this.intervalId);
    }
  }
}
```

### Hooks

#### `init`

The init is the first function called after initialising of the custom element

#### `shouldUpdate`

Before a property is set to a new value

#### `updated`

After a property value is set to a new value

#### `renderStart`

When the custom element start the rendering 

#### `renderDone`

When the custom element is finished rendering

#### `attributeChangedCallback`

Call `super.attributeChangedCallback()` first.

## CustomElementController

The `CustomElementController` is an abstract base class that allows you to create reusable controllers that can be attached to custom elements. Controllers have access to the host element's lifecycle and can trigger renders.

### Creating a Controller

Controllers must extend the `CustomElementController` class and can implement the `ICustomElementController` interface methods:

``` typescript
import { CustomElementController, CustomElement } from 'p-elements-core';

class MyController extends CustomElementController {
  // Called when the controller is initialized
  init?(): void {
    console.log('Controller initialized');
  }

  // Called when the host element connects to the DOM
  connected?(): void {
    console.log('Host connected');
  }

  // Called when the host element disconnects from the DOM
  disconnected?(): void {
    console.log('Host disconnected');
  }

  // Called before the host element renders
  hostRenderStart?(): void {
    console.log('Host render starting');
  }

  // Called after the host element finishes rendering
  hostRenderDone?(): void {
    console.log('Host render done');
  }
}
```

### Using a Controller

Create an instance of your controller in the custom element constructor or init method, passing the host element:

``` typescript
@CustomElementConfig({
  tagName: "my-element",
})
class MyElement extends CustomElement {
  private myController: MyController;

  init() {
    this.myController = new MyController(this);
  }

  render = () => {
    return <div>Element with controller</div>;
  }
}
```

The controller is automatically registered with the host element when constructed.

### Controller Methods

Controllers have access to the following methods:

- `renderNow()`: Triggers an immediate render of the host element
- `scheduleRender()`: Schedules a render of the host element
- `hostElement`: Property that returns the host custom element

### Example: Timer Controller

``` typescript
class TimerController extends CustomElementController {
  private intervalId?: number;

  connected() {
    this.intervalId = window.setInterval(() => {
      console.log('Timer tick');
      this.scheduleRender(); // Update the host element
    }, 1000);
  }

  disconnected() {
    if (this.intervalId) {
      clearInterval(this.intervalId);
    }
  }
}
```
