> For the complete documentation index, see [llms.txt](https://docs.devolutions.net/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.devolutions.net/powershell-universal/es/aplicaciones/components/custom-components/building-custom-components.md).

# Creación de componentes JavaScript personalizados

Universal es extensible y puede crear componentes y marcos de trabajo JavaScript personalizados. Este documento explica cómo crear componentes personalizados que se integren con la plataforma de aplicaciones de Universal.

{% hint style="warning" %}
Este es un tema avanzado y no es necesario si solo desea utilizar Universal Apps.
{% endhint %}

{% hint style="info" %}
Para ver un ejemplo funcional completo, consulte el proyecto [ud-mermaid](https://github.com/ironmansoftware/ud-mermaid) en GitHub. Se trata de un componente personalizado listo para producción que integra la biblioteca de diagramas Mermaid con PowerShell Universal.
{% endhint %}

## Descripción general

La creación de un componente React personalizado para PowerShell Universal implica varios elementos clave:

1. **Estructura del proyecto**: un proyecto de Node.js con Webpack para empaquetar componentes React
2. **Componente React (JSX)**: el componente React que representa su interfaz de usuario
3. **Módulo de PowerShell (PSM1)**: funciones de PowerShell que crean definiciones de componentes
4. **Manifiesto del módulo (PSD1)**: metadatos estándar del módulo de PowerShell
5. **Proceso de compilación**: configuración de Webpack para empaquetar los recursos JavaScript
6. **Registro de componentes**: registro de su componente con Universal Dashboard

### Cómo funciona

La integración entre PowerShell y React funciona así:

1. **Lado de PowerShell**: su función de PowerShell devuelve una tabla hash con las propiedades del componente
2. **Registro de recursos**: el JavaScript empaquetado se registra con el AssetService de PowerShell Universal
3. **Tipo de componente**: la propiedad `type` vincula la tabla hash de PowerShell con el componente React
4. **Paso de props**: las propiedades de la tabla hash se convierten automáticamente en props de React
5. **Representación**: Universal Dashboard carga el paquete de JavaScript y representa el componente React

```
PowerShell Function → Hashtable → Asset Service → React Component → DOM
```

### El ejemplo ud-mermaid

A lo largo de esta guía haremos referencia al proyecto [ud-mermaid](https://github.com/ironmansoftware/ud-mermaid) como ejemplo real. Este componente encapsula la biblioteca de diagramas Mermaid.js para su uso en PowerShell Universal y demuestra:

* Integración de bibliotecas JavaScript de terceros
* Uso de hooks de React (useEffect, useRef)
* Paso de objetos de configuración
* Estructura de proyecto profesional
* Automatización de la compilación

## Paso a paso

La siguiente sección le guiará paso a paso por los distintos aspectos de la creación de un componente de aplicación de Universal.

### 1. Instalación de dependencias

Deberá instalar las siguientes dependencias antes de crear su componente.

* [NodeJS](https://nodejs.org/en/): necesario para npm y para ejecutar las herramientas de compilación

### 2. Crear un nuevo proyecto

Cree un nuevo directorio para el proyecto de su componente:

```powershell
New-Item -Path .\MyComponent -ItemType Directory
Set-Location .\MyComponent
```

Inicialice un nuevo proyecto npm:

```powershell
npm init -y
```

Esto crea una estructura de proyecto básica que incluye:

* `package.json`: dependencias de Node.js y scripts de compilación

### 3. Instalar las dependencias de JavaScript

Instale las herramientas de compilación y las dependencias de React necesarias:

```powershell
npm install --save-dev @babel/core @babel/preset-env @babel/preset-react babel-loader webpack webpack-cli css-loader style-loader @babel/plugin-proposal-class-properties @babel/plugin-syntax-dynamic-import --legacy-peer-deps
```

Instale el paquete de Universal Dashboard:

```powershell
npm install universal-dashboard --legacy-peer-deps
```

Por ejemplo, el proyecto [ud-mermaid](https://github.com/ironmansoftware/ud-mermaid) incluye el paquete `mermaid` como dependencia adicional:

```json
"dependencies": {
    "mermaid": "^9.4.3",
    "universal-dashboard": "^1.0.1"
}
```

Instale cualquier biblioteca adicional que necesite su componente:

```powershell
npm install mermaid --legacy-peer-deps
```

### 4. Crear la estructura del proyecto

Cree los directorios necesarios para su componente:

```powershell
New-Item -Path .\Components -ItemType Directory
```

### 5. Crear su componente React

Cree un componente React en el directorio `Components/`. Su componente debe:

1. Importar desde `universal-dashboard`
2. Utilizar el HOC `withComponentFeatures` (componente de orden superior)
3. Aceptar props que coincidan con los parámetros de su función de PowerShell

**Ejemplo de ud-mermaid** (`Components/mermaid.jsx`):

```jsx
import React, { useEffect, useRef } from 'react';
import { withComponentFeatures } from 'universal-dashboard';
import mermaid from 'mermaid';

const UDMermaid = (props) => {
  const mermaidRef = useRef(null);
  const { id, diagram, config } = props;

  useEffect(() => {
    mermaid.initialize(config || {});
    
    if (mermaidRef.current) {
      mermaidRef.current.removeAttribute('data-processed');
      mermaid.contentLoaded();
    }
  }, [diagram, config]);

  return (
    <div className="mermaid" id={id} ref={mermaidRef}>
      {diagram}
    </div>
  );
};

export default withComponentFeatures(UDMermaid);
```

### 6. Registrar su componente

Cree un fichero `index.js` en el directorio `Components/` que registre su componente con Universal Dashboard:

```javascript
import UDMermaid from './mermaid';
UniversalDashboard.register("ud-mermaid", UDMermaid);
```

La cadena que pase a `register()` se convierte en la propiedad `type` que utilizará en su función de PowerShell.

### 7. Crear funciones de PowerShell

Ahora deberá escribir el código del módulo de PowerShell. Tendrá que actualizar el fichero PSM1 para cargar los recursos y definir funciones que creen definiciones de componentes.

El fichero PSM1 debe:

1. Registrar el fichero JavaScript empaquetado con el AssetService
2. Definir funciones que devuelvan tablas hash con las propiedades del componente

**Ejemplo de ud-mermaid** (`UniversalDashboard.Mermaid.psm1`):

```powershell
# Register JavaScript assets with PowerShell Universal
Get-ChildItem "$PSScriptRoot\*.js" | ForEach-Object {
    $Item = [UniversalDashboard.Services.AssetService]::Instance.RegisterAsset($_.FullName)
    if ($_.Name.StartsWith("index.") -and $_.Name.EndsWith(".bundle.js")) {
        $AssetId = $Item
    }
}

function New-UDMermaid {
    param(
        [Parameter()]
        [string]$Id = (New-Guid).ToString(),
        [Parameter(Mandatory)]
        [string]$Diagram,
        [Parameter()]
        [hashtable]$Config
    )

    @{
        assetId = $AssetId 
        isPlugin = $true 
        type = "ud-mermaid"  # This matches the name used in UniversalDashboard.register()
        id = $Id
        diagram = $Diagram
        config = $Config
    }
}
```

**Propiedades clave de la tabla hash:**

* `assetId`: el ID devuelto por RegisterAsset
* `isPlugin`: siempre debe establecerse en `$true` para los componentes personalizados
* `type`: debe coincidir con el nombre que utilizó en `UniversalDashboard.register()`
* `id`: un identificador único para la instancia del componente
* Las propiedades adicionales se pasan como props a su componente React

### 8. Configurar Webpack

Su `webpack.config.js` debe empaquetar sus componentes y externalizar las dependencias de React y Universal Dashboard. Esta es la configuración esencial de ud-mermaid:

```javascript
module.exports = (env) => {
  return {
    entry: {
      'index': __dirname + '/components/index.js'
    },
    output: {
      path: BUILD_DIR,
      filename: '[name].[hash].bundle.js',
      library: 'udcomponent',
      libraryTarget: 'var'
    },
    module: {
      rules: [
        { test: /\.(js|jsx)$/, exclude: [/public/], loader: 'babel-loader' },
        { test: /\.css$/, loader: "style-loader!css-loader" }
      ]
    },
    externals: {
      'react': 'react',
      'react-dom': 'reactdom',
      UniversalDashboard: 'UniversalDashboard'
    },
    resolve: {
      extensions: ['.js', '.jsx']
    }
  };
}
```

**Externals importantes:**

* `react` y `react-dom`: proporcionados por PowerShell Universal
* `UniversalDashboard`: el objeto global de Universal Dashboard

### 8.1. Configurar Babel

Cree un fichero `.babelrc` para configurar la transformación de JSX y de JavaScript moderno:

```json
{
  "presets": [
    ["@babel/preset-env", {
      "targets": {
        "browsers": [">0.5%", "not dead"]
      }
    }],
    "@babel/preset-react"
  ],
  "plugins": [
    "@babel/plugin-proposal-class-properties",
    "@babel/plugin-syntax-dynamic-import",
    "@babel/plugin-proposal-optional-chaining",
    "@babel/plugin-proposal-nullish-coalescing-operator"
  ]
}
```

Esta configuración:

* Transforma JSX en JavaScript
* Transpila JavaScript moderno para garantizar la compatibilidad con los navegadores
* Habilita funciones útiles del lenguaje JavaScript

### 9. Crear el manifiesto del módulo

Cree un manifiesto estándar de módulo de PowerShell (`.psd1`) con los metadatos de su componente:

```powershell
@{
    RootModule = 'UniversalDashboard.Mermaid.psm1'
    ModuleVersion = '1.0.0'
    Author = 'Your Name'
    Description = 'Custom component description'
    FunctionsToExport = @('New-UDMermaid')
}
```

### 10. Compilar el proyecto

Ahora puede compilar su proyecto. Generará un módulo que podrá cargar en PowerShell Universal.

Primero, añada scripts de compilación a su `package.json`:

```json
"scripts": {
    "build": "webpack -p --env production",
    "dev": "webpack-dev-server --config webpack.config.js -p --env development"
}
```

A continuación, ejecute la compilación:

```powershell
npm run build
```

**Opcional: cree un script de compilación** (como el `component.build.ps1` de ud-mermaid):

```powershell
# component.build.ps1
$OutputPath = "$PSScriptRoot\output"

Remove-Item -Path $OutputPath -Force -ErrorAction SilentlyContinue -Recurse
Remove-Item -Path "$PSScriptRoot\public" -Force -ErrorAction SilentlyContinue -Recurse

npm install --legacy-peer-deps
npm run build

New-Item -Path $OutputPath -ItemType Directory

Copy-Item $PSScriptRoot\public\*.* $OutputPath
Copy-Item $PSScriptRoot\UniversalDashboard.MyComponent.psd1 $OutputPath
Copy-Item $PSScriptRoot\UniversalDashboard.MyComponent.psm1 $OutputPath
```

Después ejecútelo:

```powershell
.\component.build.ps1
```

El proceso de compilación:

1. Empaqueta todo el código JavaScript/React con Webpack
2. Genera ficheros empaquetados con nombres con hash (por ejemplo, `index.78a6d857.bundle.js`)
3. Copia los ficheros del módulo (`.psm1`, `.psd1`) en el directorio de salida

### 11. Utilizar en PowerShell Universal

Dentro de su aplicación, cargue su módulo y ejecute la función.

```powershell
Import-Module .\output\UniversalDashboard.Mermaid.psd1

New-UDApp -Content {
   New-UDMermaid -Diagram @"
graph TD
    A[Start] --> B[Process]
    B --> C[End]
"@
}
```

## Ejemplo de estructura de proyecto

Esta es la estructura típica de un proyecto de componente personalizado (de [ud-mermaid](https://github.com/ironmansoftware/ud-mermaid)):

```
project/
├── Components/
│   ├── index.js              # Component registration
│   └── mermaid.jsx           # React component
├── output/                   # Build output (git ignored)
│   ├── index.[hash].bundle.js
│   ├── UniversalDashboard.Mermaid.psm1
│   └── UniversalDashboard.Mermaid.psd1
├── package.json              # Node.js dependencies
├── webpack.config.js         # Webpack configuration
├── component.build.ps1       # Optional build script
├── UniversalDashboard.Mermaid.psm1   # PowerShell module
└── UniversalDashboard.Mermaid.psd1   # Module manifest
```

## Props

Las props son valores que se pasan desde la tabla hash de PowerShell proporcionada por el usuario o desde la función de orden superior `withComponentsFeature` de Universal App.

### Estándar

Las propiedades que establezca en su tabla hash en PowerShell se enviarán automáticamente como props al componente React.

Por ejemplo, si establece las propiedades `diagram` y `config` en la tabla hash:

```powershell
function New-UDMermaid {
    param(
        [Parameter()]
        [string]$Id = (New-Guid).ToString(),
        [Parameter(Mandatory)]
        [string]$Diagram,
        [Parameter()]
        [hashtable]$Config
    )

    @{
        type = "ud-mermaid"
        isPlugin = $true
        assetId = $AssetId 
        id = $Id
        diagram = $Diagram
        config = $Config
    }
}
```

Entonces tendrá acceso a esas props en React:

```javascript
import React, { useEffect, useRef } from 'react';
import { withComponentFeatures } from 'universal-dashboard';
import mermaid from 'mermaid';

const UDMermaid = (props) => {
  const { id, diagram, config } = props;

  useEffect(() => {
    mermaid.initialize(config || {});
  }, [diagram, config]);

  return <div className="mermaid" id={id}>{diagram}</div>;
};

export default withComponentFeatures(UDMermaid);
```

**Buenas prácticas para las props:**

* Utilice nombres de props descriptivos que coincidan con los nombres de los parámetros de PowerShell
* Gestione las props opcionales con valores predeterminados o lógica condicional
* Las tablas hash de PowerShell se convierten automáticamente en objetos de JavaScript

### Hooks de React y ciclo de vida de los componentes

Al crear componentes personalizados, puede utilizar todos los hooks estándar de React. El componente ud-mermaid demuestra el uso de `useEffect` y `useRef` para gestionar el ciclo de vida del componente y las referencias al DOM:

```javascript
import React, { useEffect, useRef } from 'react';

const UDMermaid = (props) => {
  const mermaidRef = useRef(null);
  const { diagram, config } = props;

  // Run when diagram or config changes
  useEffect(() => {
    mermaid.initialize(config || {});
    
    if (mermaidRef.current) {
      mermaidRef.current.removeAttribute('data-processed');
      mermaid.contentLoaded();
    }
  }, [diagram, config]); // Dependency array

  return <div ref={mermaidRef}>{diagram}</div>;
};
```

**Patrones habituales:**

* `useEffect`: para la inicialización, la limpieza y la respuesta a cambios en las props
* `useRef`: para acceder directamente a elementos del DOM
* `useState`: para gestionar el estado interno del componente
* `useMemo` / `useCallback`: para optimizar el rendimiento

### Endpoints

Los endpoints son especiales por la forma en la que se registran y en la que se pasan como props a su componente. Deberá llamar a `Register` en el endpoint en PowerShell y pasar las variables Id y PSCmdlet.

```powershell
function New-UD95Button {
    param(
        [Parameter()]
        [string]$Id = [Guid]::NewGuid(),
        [Parameter()]
        [string]$Text,
        [Parameter()]
        [Endpoint]$OnClick
    )

    if ($OnClick)
    {
        $OnClick.Register($Id, $PSCmdlet)
    }

    @{
        type = "ud95-button"
        isPlugin = $true 
        assetId = $AssetId

        id = $Id 
        text = $Text 
        onClick = $OnClick
    }
}
```

Los endpoints se crean a partir de ScriptBlocks y se ejecutan cuando se produce ese evento.

```powershell
New-UD95Button -Text 'Hello' -OnClick {
    Show-UDToast -Message 'Test' 
}
```

Universal conectará automáticamente el endpoint a una función en JavaScript. Esto significa que puede utilizar las props para llamar a ese endpoint.

Observe la llamada a la función `props.onClick`. Esto llamará automáticamente al bloque de script de PowerShell en el servidor.

```javascript
import React from 'react';
import { withComponentFeatures } from 'universal-dashboard';
import { Button } from 'react95';

const UD95Button = props => {

    const p = {
        onClick: () => props.onClick()
    }

    return <Button {...p}>{props.text}</Button>
}

export default withComponentFeatures(UD95Button);
```

### setState

La prop `setState` se utiliza para establecer el estado del componente. Esto garantiza que se realice el seguimiento del estado y que su componente funcione con `Get-UDElement`.

Por ejemplo, con un campo de texto, deberá llamar a `props.setState` y pasar el nuevo valor de texto para el estado.

```javascript
const UDTextField = (props) => {
    const onChange = (e) => {
        props.setState({value: e.target.value})
    }

    return <TextField  {...props} onChange={onChange} />
}

export default withComponentFeatures(UDTextField);
```

### children

La prop `children` es una prop estándar de React. Si su componente admite elementos secundarios, como una lista o un cuadro de selección, debe utilizar la prop estándar `props.children` para garantizar que los cmdlets `Add-UDElement`, `Remove-UDElement` y `Clear-UDElement` funcionen correctamente.

## Resolución de problemas y depuración

### Problemas habituales

**El componente no se renderiza:**

1. Verifique que el `type` de su función de PowerShell coincida con el nombre de `UniversalDashboard.register()`
2. Compruebe que el recurso esté correctamente registrado con AssetService
3. Asegúrese de que `isPlugin` esté establecido en `$true`
4. Confirme que el fichero JavaScript empaquetado existe en el directorio del módulo

**Las props no se pasan correctamente:**

1. Verifique que los nombres de las propiedades coincidan entre la tabla hash de PowerShell y el componente de React
2. Consulte la consola del navegador para ver si hay errores de JavaScript
3. Utilice React DevTools para inspeccionar las props del componente

**Fallos de compilación:**

1. Ejecute `npm install --legacy-peer-deps` para asegurarse de que las dependencias estén instaladas
2. Compruebe si hay errores de sintaxis en los ficheros JSX
3. Verifique que los externals de webpack.config.js estén configurados correctamente
4. Asegúrese de que babel esté configurado correctamente para la transformación de JSX

### Consejos de depuración

**Consola del navegador:** Abra las herramientas de desarrollo del navegador (F12) para ver los errores y advertencias de JavaScript.

**React DevTools:** Instale la extensión de navegador React DevTools para inspeccionar la jerarquía y las props de los componentes.

**Depuración de PowerShell:** Utilice `Write-Host` o `Write-Debug` en sus funciones de PowerShell para rastrear la ejecución.

**Servidor de desarrollo de webpack:** Durante el desarrollo, utilice webpack-dev-server para la recarga en caliente:

```powershell
npm run dev
```

A continuación, configure PowerShell Universal para que cargue desde la URL del servidor de desarrollo.

## Ejemplo: flujo de trabajo completo de un componente

Aquí tiene un ejemplo completo basado en el proyecto [ud-mermaid](https://github.com/ironmansoftware/ud-mermaid):

**1. Cree el componente de React** (`Components/mermaid.jsx`):

```jsx
import React, { useEffect, useRef } from 'react';
import { withComponentFeatures } from 'universal-dashboard';
import mermaid from 'mermaid';

const UDMermaid = (props) => {
  const mermaidRef = useRef(null);
  const { id, diagram, config } = props;

  useEffect(() => {
    mermaid.initialize(config || {});
    if (mermaidRef.current) {
      mermaidRef.current.removeAttribute('data-processed');
      mermaid.contentLoaded();
    }
  }, [diagram, config]);

  return <div className="mermaid" id={id} ref={mermaidRef}>{diagram}</div>;
};

export default withComponentFeatures(UDMermaid);
```

**2. Registre el componente** (`Components/index.js`):

```javascript
import UDMermaid from './mermaid';
UniversalDashboard.register("ud-mermaid", UDMermaid);
```

**3. Cree la función de PowerShell** (`UniversalDashboard.Mermaid.psm1`):

```powershell
Get-ChildItem "$PSScriptRoot\*.js" | ForEach-Object {
    $Item = [UniversalDashboard.Services.AssetService]::Instance.RegisterAsset($_.FullName)
    if ($_.Name.StartsWith("index.") -and $_.Name.EndsWith(".bundle.js")) {
        $AssetId = $Item
    }
}

function New-UDMermaid {
    param(
        [Parameter()]
        [string]$Id = (New-Guid).ToString(),
        [Parameter(Mandatory)]
        [string]$Diagram,
        [Parameter()]
        [hashtable]$Config
    )

    @{
        assetId = $AssetId 
        isPlugin = $true 
        type = "ud-mermaid"
        id = $Id
        diagram = $Diagram
        config = $Config
    }
}
```

**4. Compile y pruebe**:

```powershell
# Build
npm run build

# Test in PowerShell Universal
Import-Module .\output\UniversalDashboard.Mermaid.psd1

New-UDApp -Content {
    New-UDMermaid -Diagram @"
graph TD
    A[Christmas] -->|Get money| B(Go shopping)
    B --> C{Let me think}
    C -->|One| D[Laptop]
    C -->|Two| E[iPhone]
    C -->|Three| F[Car]
"@
}
```

## Recursos adicionales

* **Repositorio de GitHub ud-mermaid**: <https://github.com/ironmansoftware/ud-mermaid> - Ejemplo completo funcional
* **Documentación de React**: <https://react.dev>
* **Documentación de Webpack**: <https://webpack.js.org>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.devolutions.net/powershell-universal/es/aplicaciones/components/custom-components/building-custom-components.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
