# Decorators

Ignite-Element provides powerful `@Shared` and `@Isolated` decorators for building modular, scalable web components. These decorators streamline integrating state management with rendering logic, allowing you to create maintainable and reusable components.

***

### **Enable Decorators in TypeScript**

To use decorators in your project, make sure the following options are enabled in your `tsconfig.json`:

```json
{
  "compilerOptions": {
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
  }
}
```

Without these settings, TypeScript will not recognize decorators in your code.

***

### **Type Safety with `RenderArgs`**

Ignite-Element provides the `RenderArgs` type helper to make your components type-safe when using decorators. You’ll need to pass `typeof source` (e.g., your state machine or store) to `RenderArgs` to ensure type inference.

**Future Plans**: We aim to infer types automatically for Ignite-Element decorators in the future, simplifying the setup further.

***

### What Are Decorators

* **`@Shared`**: Creates components that share the same state across all instances. Ideal for global states like progress bars, dashboards, or game stats.
* **`@Isolated`**: Creates components with independent states for each instance. Perfect for localized states like tab navigation, rating systems, or dynamic forms.

***

### Shared Decorator

#### **Overview**

The `@Shared` decorator connects a component to a global state, ensuring all component instances reflect and update the same state.

#### Example: Progress Bar Component

The following example demonstrates a progress bar that calculates the percentage of completed tasks based on the global state managed by a state machine.

{% tabs %}
{% tab title="JavaScript" %}

```javascript
import { Shared, RenderArgs } from "ignite-element";
import { html } from "lit-html";
import taskManagerMachine from "./taskManagerMachine";

@Shared("progress-bar")
export class ProgressBar {
  render({ state }: RenderArgs<typeof taskManagerMachine>) {
    const { tasks } = state.context;
    const completed = tasks.filter((t) => t.completed).length;
    const total = tasks.length;
    const percentage = total > 0 ? (completed / total) * 100 : 0;

    const backgroundStyle =
      percentage === 100
        ? "background: #22c55e;"
        : `background: linear-gradient(
            90deg,
            rgba(34, 197, 94, 1) 0%,
            rgba(251, 191, 36, 1) 50%,
            rgba(209, 213, 219, 0.1) 100%
          );`;

    return html`
      <div class="p-4 bg-blue-100 border rounded-md mt-2 mb-2">
        <h3 class="text-lg font-bold">Progress</h3>
        <div class="w-full bg-gray-200 rounded-full h-4 overflow-hidden">
          <div
            class="h-4 rounded-full transition-all duration-700 ease-out"
            style="width: ${percentage}%; ${backgroundStyle}"
          ></div>
        </div>
        <p class="mt-2">${completed}/${total} tasks completed</p>
      </div>
    `;
  }
}

```

{% endtab %}
{% endtabs %}

***

### Isolated Decorator

#### **Overview**

The `@Isolated` decorator connects a component to a state that is independent for each instance. This ensures that multiple component instances can coexist with unique behaviors and states.

#### **Example 2: Rating Component**

The `RatingItem` component tracks its rating state independently for each instance.

{% tabs %}
{% tab title="JavaScript" %}

```javascript
import { Isolated } from "ignite-element";
import { RenderArgs } from "ignite-element";
import ratingMachine from "./ratingMachine";

@Isolated("rating-item")
export class RatingItem {
  render({ state, send }: RenderArgs<typeof ratingMachine>) {
    const { rating, maxRating } = state.context;

    return html`
      <div class="p-4 bg-white border rounded-md">
        <h3 class="font-bold mb-2">Rate this item:</h3>
        <div class="flex space-x-2">
          ${Array.from({ length: maxRating }, (_, i) =>
            html`
              <button
                class="text-xl ${rating > i ? 'text-yellow-500' : 'text-gray-300'}"
                @click=${() => send({ type: "RATE", value: i + 1 })}
              >
                ★
              </button>
            `
          )}
        </div>
        <p class="mt-2 text-sm text-gray-600">
          You rated this ${rating}/${maxRating}.
        </p>
      </div>
    `;
  }
}

```

{% endtab %}
{% endtabs %}

***

### Best Practices

1. **Centralize Logic**:
   * Use state machines or stores to manage all data and interactions.
2. **Choose the Right Decorator**:
   * Use `@Shared` for global states (e.g., dashboards, progress bars).
   * Use `@Isolated` for independent states (e.g., tabs, ratings).
3. **Dynamic Contexts**:
   * Configure state machines dynamically for greater flexibility.
4. **Encapsulate Rendering**:
   * Keep rendering logic focused and delegate state management to the source.
