# Posicionamiento de UI

Para todo tipo de contenido de UI, use el `uiTransform` componente para establecer el tamaño, la posición y otras propiedades relacionadas con la alineación de la entidad.

El `uiTransform` El componente funciona en el espacio 2D de la pantalla de manera muy similar a como el `Transform` componente funciona en el espacio 3D de la escena.

***archivo ui.tsx:***

```ts
import { UiEntity, ReactEcs } from '@dcl/sdk/react-ecs'
import { Color4 } from '@dcl/sdk/math'

export const uiMenu = () => (
	<UiEntity
		uiTransform={{
			width: '200px',
			height: '100px',
			justifyContent: 'center',
			alignItems: 'center',
		}}
		uiBackground={{ color: Color4.Green() }}
	/>
)
```

***archivo index.ts:***

```ts
import { ReactEcsRenderer } from '@dcl/sdk/react-ecs'
import { uiMenu } from './ui'

export function main() {
    ReactEcsRenderer.setUiRenderer(uiMenu, { virtualWidth: 1920, virtualHeight: 1080 })
}
```

{% hint style="warning" %}
**📔 Nota**: Todos los fragmentos siguientes en esta página asumen que tienes un `.ts` similar al anterior, ejecutando la `ReactEcsRenderer.setUiRenderer()` función.
{% endhint %}

## Propiedades de posicionamiento

La alineación de las entidades de UI se basa en el modelo de alineación de Flexbox. Este es un modelo muy potente para organizar dinámicamente entidades anidadas dentro de modales que pueden variar en tamaño.

{% hint style="info" %}
**💡 Tip**: La implementación de UI de Decentraland se basa en la de [Yoga](https://yogalayout.com/docs/). Lea [este artículo](https://www.joshwcomeau.com/css/interactive-guide-to-flexbox/) para una cobertura muy accesible y profunda de las propiedades disponibles en Flexbox.
{% endhint %}

### Tamaño de la entidad

Usa `width` y `height` para establecer el tamaño de la entidad. Se admiten los siguientes tipos de valores:

* `auto`: El tamaño se adapta para ajustarse al contenido interior. Esto es muy conveniente para texto que puede variar en longitud. Escriba el valor como "auto".
* **Porcentaje**: Como un porcentaje de las medidas del elemento padre. Escriba el valor como una cadena que termine en "%", por ejemplo `10 %`.
* **Píxeles**: Escriba el valor como un número.
* **Ancho o alto de la pantalla**: Se pueden usar vw (view width) y vh (view height) para indicar una fracción del tamaño completo de la ventana que ejecuta Decentraland. Por ejemplo `10vw` se refiere al 10% del ancho de la ventana, `25vh` al 25% de la altura de la ventana.

Tenga en cuenta que estas propiedades afectan el **default** tamaño de ese elemento, el tamaño del elemento antes de que se realicen los cálculos de flex grow y flex shrink. El tamaño final puede interpretarse de forma diferente según el tamaño del elemento padre y las propiedades de Flexbox que estén establecidas.

{% hint style="warning" %}
**📔 Nota**: En propiedades que admiten tanto números como cadenas, para establecer el valor en píxeles, escriba un número. Para establecer estos campos como un porcentaje de las medidas del padre, escriba el valor como una cadena que termine en "%", por ejemplo `10 %`. También puede establecer un valor en píxeles como una cadena terminando la cadena en `px`, por ejemplo `200px`.

* Cuando los valores se expresan como un porcentaje, siempre están en relación con el contenedor del padre. Si la entidad no tiene padres, entonces el valor es un porcentaje de toda la pantalla.
* Si los valores se expresan en píxeles, son absolutos y no se ven afectados por la escala del padre.
* Si los valores se expresan en `vh` o `vw`, son un porcentaje de la ventana completa, no afectados por la escala del padre.

Para el `auto` Para que width/height funcione, se aplican las siguientes reglas:

* El UiTransform que usa width/height como “auto” debe tener `alignSelf`: `“center”`/`“flex-start”`/`“flex-end”` O `positionType: “absolute”`
* Si el UiTransform de un hijo usa `positionType: “absolute”`, el padre no se adaptará a su tamaño/posición
* Si el UiTransform de un hijo utiliza cualquier sobrescritura de posición, el padre no se adaptará a su tamaño/posición
  {% endhint %}

Estas otras propiedades también están disponibles para ajustar el tamaño de una forma más avanzada:

* `maxWidth` y `maxHeight`: *número* o cadena (como height y width). El tamaño máximo que la entidad puede tener.
* `minWidth` y `minHeight`: *número* o cadena (como height y width). El tamaño mínimo que la entidad puede tener. Si el padre es demasiado pequeño para ajustarse al tamaño mínimo de las entidades, estas desbordarán su padre.
* `flexBasis`: Esta es una forma independiente del eje de proporcionar el tamaño predeterminado de un elemento a lo largo del eje principal. Establecer el flex basis de un hijo es similar a establecer el width de ese hijo si su padre es un contenedor con flex direction: row o establecer el height de un hijo si su padre es un contenedor con flex direction: column.

```ts
import { UiEntity, ReactEcs } from '@dcl/sdk/react-ecs'
import { Color4 } from '@dcl/sdk/math'

export const uiMenu = () => (
	<UiEntity
		uiTransform={{
			alignSelf: 'center',
			alignContent: 'center',
			width: '80%',
			height: '30%',
			minWidth: 300,
			maxWidth: 2500,
			margin: { left: '10%', right: '10%' },
		}}
		uiBackground={{ color: Color4.Green() }}
	/>
)
```

### Organización de entidades hijas

Por defecto, las entidades hijas se posicionan en relación con la esquina superior izquierda de su padre. Puede usar propiedades como `justifyContent` y `alignItems` para cambiar este comportamiento.

{% hint style="info" %}
**💡 Tip**: Cualquier propiedad que se refiera a *content* se refiere a las entidades a lo largo del eje principal (determinado por `flexDirection`). Cualquier propiedad que se refiera
{% endhint %}

* `flexDirection`: Flex direction controla la dirección en que se disponen los hijos de un nodo. Esto también se conoce como el eje principal. El eje principal es la dirección en la que se colocan los hijos. El eje cruzado es el eje perpendicular al eje principal, o el eje en el que se disponen las líneas de ajuste. Toma su valor del tipo `FlexDirectionType` type. Están disponibles las siguientes opciones:
  * `row` (DEFAULT)
  * `row-reverse`
  * `column`
  * `column-reverse`
* `justifyContent`: Esta propiedad describe cómo alinear a los hijos dentro del eje principal de su contenedor. Por ejemplo, puede usar esta propiedad para centrar un hijo horizontalmente dentro de un contenedor con `flexDirection` configurado en row o verticalmente dentro de un contenedor con `flexDirection` configurado en column. El valor de esta propiedad debe ser del tipo `AlignType` type. Los valores posibles son:
  * `flex-start` (DEFAULT): Alinear los hijos de un contenedor al inicio del eje principal del contenedor.
  * `flex-end`: Alinear los hijos de un contenedor al final del eje principal del contenedor.
  * `center`: Alinear los hijos de un contenedor en el centro del eje principal del contenedor.
  * `space-between`: Distribuir uniformemente a los hijos a lo largo del eje principal del contenedor, repartiendo el espacio restante entre los hijos.
  * `space-around`: Distribuir uniformemente a los hijos a lo largo del eje principal del contenedor, repartiendo el espacio restante alrededor de los hijos. En comparación con space-between, usar space-around resultará en espacio distribuido al inicio del primer hijo y al final del último hijo.
* `alignItems`: Describe cómo alinear a los hijos a lo largo del eje cruzado de su contenedor. align items es muy similar a justify content pero en vez de aplicarse al eje principal, align items se aplica al eje cruzado. Esta propiedad requiere un valor de `AlignType` type. Están disponibles las siguientes opciones:
  * `stretch`: (DEFAULT) Estirar a los hijos de un contenedor para que coincidan con la altura del eje cruzado del contenedor.
  * `flex-start`: Alinear los hijos de un contenedor al inicio del eje cruzado del contenedor.
  * `flex-end`: Alinear los hijos de un contenedor al final del eje cruzado del contenedor.
  * `center`: Alinear los hijos de un contenedor en el centro del eje cruzado del contenedor.
  * `baseline`: Alinear los hijos de un contenedor a lo largo de una línea de base común. Los hijos individuales pueden establecerse como la línea de base de referencia para sus padres.
* `alignSelf`: Align self tiene las mismas opciones y efecto que `alignItems` pero en lugar de afectar a los hijos dentro de un contenedor, puede aplicar esta propiedad a un solo hijo para cambiar su alineación dentro de su padre. align self anula cualquier opción establecida por el padre con align items. Toma su valor de `AlignType`, ver `alignItems` arriba para detalles sobre estas opciones.
* `alignContent`: Align content define la distribución de líneas a lo largo del eje cruzado. Esto solo tiene efecto cuando los elementos se envuelven en múltiples líneas usando `flexWrap`. Toma su valor de `AlignType` type. Están disponibles las siguientes opciones:
  * `flex-start`: (DEFAULT) Alinear las líneas envueltas al inicio del eje cruzado del contenedor.
  * `flex-end`: Alinear las líneas envueltas al final del eje cruzado del contenedor.
  * `stretch`: Estirar las líneas envueltas para que coincidan con la altura del eje cruzado del contenedor.
  * `center`: Alinear las líneas envueltas en el centro del eje cruzado del contenedor.
  * `space-between`: Distribuir uniformemente las líneas envueltas a lo largo del eje principal del contenedor, repartiendo el espacio restante entre las líneas.
  * `space-around`: Distribuir uniformemente las líneas envueltas a lo largo del eje principal del contenedor, repartiendo el espacio restante alrededor de las líneas. En comparación con space-between, usar space-around resultará en espacio distribuido al inicio de las primeras líneas y al final de la última línea.
* `flexGrow`: Esto describe cómo se debe distribuir cualquier espacio dentro de un contenedor entre sus hijos a lo largo del eje principal. Después de colocar a sus hijos, un contenedor distribuirá cualquier espacio restante de acuerdo con los valores de flex grow especificados por sus hijos. Flex grow acepta cualquier valor de punto flotante >= 0, siendo 0 el valor por defecto. Un contenedor distribuirá cualquier espacio restante entre sus hijos ponderado por el valor de flex grow de cada hijo.
* `flexShrink`: Describe cómo reducir el tamaño de los hijos a lo largo del eje principal en el caso de que el tamaño total de los hijos desborde el tamaño del contenedor en el eje principal. flex shrink es muy similar a flex grow y puede pensarse de la misma manera si cualquier tamaño desbordante se considera espacio restante negativo. Estas dos propiedades también funcionan bien juntas permitiendo que los hijos crezcan y se encojan según sea necesario. Flex shrink acepta cualquier valor de punto flotante >= 0, siendo 1 el valor por defecto. Un contenedor reducirá el tamaño de sus hijos ponderado por el valor de flex shrink del hijo.
* `overflow`: Determina qué ocurre si el tamaño de los hijos de una entidad desborda su padre. Usa valores del `OverflowType` tipo.
  * `hidden`: Las entidades desbordadas se hacen invisibles.
  * `visible`: Las entidades desbordadas se salen de los márgenes del padre.
* `flexWrap`: La propiedad flex wrap se establece en contenedores y controla qué ocurre cuando los hijos desbordan el tamaño del contenedor a lo largo del eje principal. Por defecto los hijos se fuerzan en una sola línea (lo que puede encoger entidades). Si se permite wrapping, los elementos se envuelven en múltiples líneas a lo largo del eje principal si es necesario. wrap-reverse se comporta igual, pero el orden de las líneas se invierte. Esta propiedad toma su valor de `FlexWrapType` tipo.
  * `wrap`
  * `no-wrap`
  * `wrap-reverse`

### Márgenes y padding

* `margin`: Esta propiedad afecta al espaciado alrededor del exterior de un nodo. Un nodo con margin se desplazará respecto a los límites de su padre pero también desplazará la ubicación de cualquier hermano. El margin de un nodo contribuye al tamaño total de su padre si el padre tiene tamaño automático. Establezca espacio entre la entidad y los márgenes de su padre. El valor esperado es un objeto que contiene las propiedades `top`, `left`, `bottom`, y `right`.
* `padding`: Esta propiedad afecta al tamaño del nodo al que se aplica. El padding en Yoga actúa como si se hubiera establecido box-sizing: border-box;. Es decir, el padding no se añadirá al tamaño total de una entidad si tiene un tamaño explícito establecido. Para nodos con tamaño automático, el padding aumentará el tamaño del nodo así como desplazará la ubicación de cualquier hijo. El valor esperado es un objeto que contiene las propiedades `top`, `left`, `bottom`, y `right`.

### Ajuste fino de la posición

En Flexbox, las posiciones de las entidades se determinan principalmente por cómo están anidadas y qué propiedades de disposición están establecidas en el padre y el hijo. A menudo no es necesario establecer la propiedad `position` en absoluto. Pero si desea ajustar eso, o anular completamente el flujo normal de Flexbox y establecer una posición absoluta, aquí están las propiedades relevantes:

* `positionType`: Define cómo se posicionan las entidades. Usa un valor del `PositionType` enum.
  * `relative`: (DEFAULT) Por defecto una entidad se posiciona de forma relativa. Esto significa que una entidad se posiciona según el flujo normal del diseño y luego se desplaza respecto a esa posición en función de los valores de `top`, `right`, `bottom`, y `left`. El desplazamiento no afecta la posición de ningún hermano o entidad padre.
  * `absolute`: Cuando se posiciona de forma absoluta, una entidad no participa en el flujo normal del diseño. En su lugar se coloca de forma independiente de sus hermanos. La posición se determina en función de la `top`, `right`, `bottom`, y `left` más bajos.
* `position`: Los valores de posición `top`, `right`, `bottom`, y `left` se comportan de manera diferente dependiendo del `positionType`. Para una entidad relativa desplazan la posición de la entidad en la dirección especificada. Para una entidad absoluta, sin embargo, estas propiedades especifican el desplazamiento del lado de la entidad desde el mismo lado en el padre. El valor esperado es un objeto que contiene las propiedades `top`, `left`, `bottom`, y `right`.

{% hint style="warning" %}
**📔 Nota** : Al medir desde la parte superior, los números para `position` deben ser negativos. Ejemplo: para posicionar un componente dejando un margen de 20 píxeles con respecto al padre en los lados superior e izquierdo, establezca `position` a 20, -20.
{% endhint %}

### Visibility

* `display`: Determina si una entidad es visible o no. Para hacer una entidad invisible, establezca `display` a `none`.

### Z Index

El `zIndex` propiedad de un `UiEntity` determina el orden en que se renderizan las entidades. Las entidades con un `zIndex` más alto se renderizan encima de entidades con un `zIndex`. El `zIndex` por defecto es 0.

```ts
import { UiEntity, ReactEcs } from '@dcl/sdk/react-ecs'
import { Color4 } from '@dcl/sdk/math'

export const uiMenu = () => (
	<UiEntity
		uiTransform={{
			zIndex: 4
		}}
		uiBackground={{ color: Color4.Green() }}
	/>
)
```

{% hint style="warning" %}
**📔 Nota** : El `zIndex` La propiedad solo ordenará elementos en relación con hermanos directos, no puede usarse para renderizar una entidad encima de otras partes del árbol de diseño. En términos de html/CSS, cada elemento de UI de DCL crea un nuevo [stacking context](https://web.dev/learn/css/z-index#stacking_context).

La UI predeterminada de Decentraland, incluyendo el mapa, chat, etc., siempre se renderiza encima de todos los demás elementos de UI.
{% endhint %}

## Tamaño de UI responsivo

Jugadores con diferentes tamaños de pantalla pueden ver su diseño de UI de forma distinta. Si establece el tamaño de cualquier elemento de UI en un número fijo de píxeles, esta UI puede verse demasiado pequeña para leer en pantallas retina, que tienen una densidad de píxeles mucho mayor.

En lugar de posicionar y escalar elementos de UI en términos de porcentajes de pantalla, también puede obtener las dimensiones del canvas y luego calcular las posiciones y tamaños absolutos siguiendo su propia lógica personalizada. Por ejemplo, podría elegir diferentes disposiciones de diálogo dependiendo del tamaño de la pantalla.

Para obtener información sobre la dimensión de la pantalla, puede comprobar el `UiCanvasInformation`, que se añade por defecto a la entidad raíz de la escena.

El `UiCanvasInformation` el componente contiene la siguiente información:

* `height`: Altura del canvas en píxeles
* `width`: Ancho del canvas en píxeles
* `devicePixelRatio`: La relación entre la resolución en píxeles físicos del dispositivo y los píxeles en el canvas
* `interactableArea`: Un `BorderRect` objeto, detallando el área designada para los elementos de UI de la escena. Este objeto contiene valores para `top`, `bottom`, `left` y `right`, cada uno de estos es el número de píxeles en ese margen de la pantalla que ocupan las UI del explorer.

{% hint style="warning" %}
**📔 Nota** : Diferentes Explorer de Decentraland tendrán valores distintos para estos, ya que las UIs globales de la plataforma pueden diferir, y los valores podrían cambiar dinámicamente según el usuario expanda u oculte diferentes menús de UI globales.
{% endhint %}

```ts
export function Main(){
  let canvas = UiCanvasInformation.get(engine.RootEntity)
	console.log("CANVAS DIMENSIONS: ", canvas.width, canvas.height)
})
```

El siguiente fragmento calcula continuamente un valor multiplicador basado en el tamaño de la pantalla:

```ts
import { engine, UiCanvasInformation } from "@dcl/sdk/ecs"

let timer = 0
let canvasInfoTimer = 0.5
export let scaleFactor = 1

export function UIScaleUpdate() {

  engine.addSystem((dt) => {
    timer += dt

    if (timer <= canvasInfoTimer) return
    timer = 0

    const uiCanvasInfo = UiCanvasInformation.getOrNull(engine.RootEntity)

    if (!uiCanvasInfo) return

    const newScaleFactor = Math.min(uiCanvasInfo.width / 1920, uiCanvasInfo.height / 1080)

    if (newScaleFactor !== scaleFactor) {
      scaleFactor = newScaleFactor
      console.log('NEW UI scaleFactor: ', scaleFactor)
    }
  })
}
```

El valor de la variable `scaleFactor` que esta función actualiza, puede entonces usarse como multiplicador en cualquier elemento de UI en la escena, incluyendo `heigh`, `width` y `fontSize` más bajos.

```ts
import { UiEntity, Label, ReactEcs } from '@dcl/sdk/react-ecs'
import { scaleFactor } from './calculate-scale-factor'
import { Color4 } from '@dcl/sdk/math'

export const uiMenu = () => (
	<UiEntity
		uiTransform={{
			width: 200 * scaleFactor,
			height: 100 * scaleFactor,
			justifyContent: 'center',
			alignItems: 'center',
			padding: 4 * scaleFactor
		}}
		uiBackground={{ color: Color4.Green() }}
	>
	  	<Label
		        value={description}
		        fontSize={18 * scaleFactor}
		        textAlign="middle-center"
		        uiTransform={{
		          width: "auto",
		          height: "auto",
		          alignSelf: "center",
		          margin: { top: 10 * scaleFactor, bottom: 10 * scaleFactor },
		        }}
	      />
	</UiEntity>
)
```

Algunas otras buenas prácticas respecto a los tamaños de UI:

* Si el width o height de cualquier elemento de UI es dinámico, es bueno también usar los `maxWidth`, `minWidth`, `maxHeight`, y `minHeight` parámetros para asegurarse de que se mantengan dentro de valores razonables.
* El tamaño de fuente del texto es relativo a un número fijo de píxeles, debe hacerlo dinámico para que siga siendo legible en pantallas retina. Vea [Tamaño de texto responsive](https://docs.decentraland.org/creator/content-creator-es/scenes-sdk7/ui_text#responsive-text-size)
