Creación de un componente web retro que se puede arrastrar con iluminación

 

 

 


Índice
  1. Empezando
    1. index.html
    2. Los diversos .jsarchivos
    3. Bonificación: fuentes
  2. Parte 1: Creación de nuestro primer componente web
    1. Escribir nuestro marcado
    2. Cómo personalizar nuestro componente web
    3. Prima
    4. Breve respiro ‍
  3. Parte 2: Hacer que nuestro componente se pueda arrastrar
    1. Directivas
    2. Controladores
    3. Escribiendo nuestro controlador
  4. Part 3: Creating The Broken Window Effect
    1. Dispatching and listening to events in Lit

En el artículo de hoy, Andrico Karoulla explica cómo crear un efecto de arrastre interesante escuchando los eventos de arrastre y escribiendo alguna lógica personalizada dentro de los controladores.

 

En los años 90, mi primer sistema operativo fue Windows. Ahora, en la década de 2020, trabajo principalmente en la creación de aplicaciones web utilizando el navegador. Con el paso de los años, el navegador se ha transformado en una herramienta maravillosa y poderosa que admite un amplio mundo de aplicaciones enriquecidas. Muchas de estas aplicaciones , con sus complejas interfaces y su amplitud de capacidades, harían sonrojar incluso a los programas más resistentes del cambio de milenio.

( Vista previa grande )

Las funciones nativas del navegador, como los componentes web, están siendo adoptadas y utilizadas en toda la web por empresas multinacionales y desarrolladores individuales por igual.

 

En caso de que se pregunte si alguien está usando componentes web:

- GitHub
- YouTube
- Twitter (tweets incrustados)
- SalesForce
- ING
- Aplicación web Photoshop
- Herramientas de desarrollo de Chrome
- La interfaz de usuario completa de Firefox
- Cliente web Apple Music

– Danny Moerkerke (@dannymoerkerke) 5 de agosto de 2022

Entonces, ¿por qué no adoptar la tecnología del presente rindiendo homenaje a las interfaces del pasado?

En este artículo, espero enseñarte precisamente eso replicando el icónico efecto de ventana rota .

Usaremos componentes web , el modelo de componentes nativo del navegador, para construir esta interfaz. También usaremos la biblioteca Lit , que simplifica las API de los componentes web nativos.

Muchos de los conceptos de los que hablo aquí son lecciones que aprendí al crear A2k , una biblioteca de interfaz de usuario diseñada para ayudarte a crear una interfaz de usuario retro con herramientas modernas.

En este artículo, cubriremos:

  • los conceptos básicos de la creación de componentes web utilizando Lit;
  • cómo personalizar fácilmente el comportamiento de su componente utilizando las herramientas integradas de Lit;
  • cómo encapsular funcionalidades reutilizables;
  • cómo enviar y responder a eventos utilizando métodos avanzados de flujo de datos.

Vale la pena conocer HTML, CSS y algo de JavaScript básico para seguir este tutorial, pero no se requieren conocimientos específicos del marco.

Empezando

Puedes seguir el permiso en el navegador usando StackBlitz .

Una vez que StackBlitz termine de configurarse, debería ver lo siguiente en la ventana del navegador:

( Vista previa grande )

Nota: Si no desea utilizar StackBlitz, puede clonar el repositorio y ejecutar las instrucciones dentro del README.mdarchivo. También puede utilizar Lit VSCode para resaltar la sintaxis y las funciones.

A continuación, abra el proyecto en el editor de su elección. Echemos un vistazo rápido para ver cómo se ve nuestro código de inicio.

index.html

Tenemos un archivo HTML muy básico que hace poco más que importar algo de CSS y un archivo JavaScript.

Es posible que también hayas visto un elemento nuevo, el a2k-windowelemento. No habrás visto esto antes porque este es el elemento personalizado que construiremos nosotros mismos. Como todavía no hemos creado ni registrado este componente, el navegador volverá a mostrar el contenido HTML interno.

Los diversos .jsarchivos

Agregué un pequeño texto estándar para algunos de los componentes y funciones, pero completaremos los vacíos a lo largo de este(s) artículo(s). Importé todo el código propio y de terceros necesario que usaremos a lo largo de este artículo.

Bonificación: fuentes

¡También agregué algunas fuentes retro para divertirme! Es una maravillosa fuente inspirada en MS-2000 creada por Lou . Puedes descargarlo y usarlo en tus propios proyectos si buscas inyectar un poco de sabor milenario a tus diseños.

Parte 1: Creación de nuestro primer componente web

Escribir nuestro marcado

Lo primero que queremos hacer es conseguir un elemento de ventana de aspecto convincente. Con solo unas pocas líneas de código, tendremos lo siguiente.

( Vista previa grande )

Comencemos saltando a nuestro a2k-window.jsarchivo. Escribiremos un pequeño texto estándar para que nuestro componente esté en funcionamiento.

Necesitaremos definir una clase que extienda LitElementla clase base de Lit. Al extenderse desde LitElement, nuestra clase obtiene la capacidad de gestionar estados y propiedades reactivos. También necesitamos implementar una renderfunción en la clase que devuelva el marcado para renderizar.

Una implementación realmente básica de una clase se verá así:

class A2kWindow extends LitElement { render() { return html` div slot/slot /div `; }}

Hay dos cosas que vale la pena señalar:

  • Podemos especificar un ID de elemento que luego se encapsula dentro del componente web. Al igual que el documento de nivel superior, no se permiten ID duplicados dentro del mismo componente, pero otros componentes web o elementos DOM externos pueden usar el mismo ID.
  • El slotelemento es una herramienta útil que puede representar marcas personalizadas transmitidas desde el padre. Para aquellos familiarizados con React, podemos compararlo con un portal de React que representa el lugar donde se configura el childrenaccesorio. Puedes hacer más con él, pero eso está más allá del alcance de este artículo.

Escribir lo anterior no hace que nuestro componente web esté disponible en nuestro HTML. Necesitaremos definir un nuevo elemento personalizado para indicarle al navegador que asocie esta definición con el a2k-windownombre de la etiqueta. Debajo de nuestra clase de componente, escriba el siguiente código:

customElements.define("a2k-window", A2kWindow);

Ahora volvamos a nuestro navegador. Deberíamos esperar ver nuestro nuevo componente renderizado en la página, pero...

( Vista previa grande )

Aunque nuestro componente ha sido renderizado, vemos contenido simple y sin estilo. Sigamos adelante y agreguemos más HTML y CSS:

class A2kWindow extends LitElement { static styles = css` :host { font-family: var(--font-primary); } #window { width: min(80ch, 100%); } #panel { border: var(--border-width) solid var(--color-gray-400); box-shadow: 2px 2px var(--color-black); background-color: var(--color-gray-500); } #draggable { background: linear-gradient( 90deg, var(--color-blue-100) 0%, var(--color-blue-700) 100% ); user-select: none; } #draggable p { font-weight: bold; margin: 0; color: white; padding: 2px 8px; } [data-dragging="idle"] { cursor: grab; } [data-dragging="dragging"] { cursor: grabbing; } `; render() { return html` div div slot/slot /div /div `; }}

Hay un par de cosas que vale la pena señalar en el código anterior:

 

  • Definimos los estilos con alcance para este elemento personalizado a través de la static stylespropiedad. Debido a cómo funciona la encapsulación de estilos, nuestro componente no se verá afectado por ningún estilo externo. Sin embargo, podemos usar las variables CSS que hemos agregado en nuestro styles.csspara aplicar estilos desde una fuente externa.
  • Agregué algunos estilos para elementos DOM que aún no existen, pero los agregaremos pronto.

Una nota sobre los estilos: El estilo en Shadow DOM es un tema demasiado extenso para profundizar en este artículo. Para obtener más información sobre el estilo en Shadow DOM, puede consultar la documentación de Lit.

Si actualiza, debería ver lo siguiente:

( Vista previa grande )

Que empieza a parecerse más a nuestro componente web inspirado en Windows.

Consejo profesional: si no ves el navegador, aplica los cambios que esperas. Abra las herramientas de desarrollo del navegador. Es posible que el navegador tenga algunos mensajes de error útiles que le ayudarán a determinar dónde fallan las cosas.

Cómo personalizar nuestro componente web

Nuestro siguiente paso es crear el encabezado para nuestro componente de ventana. Una característica principal de los componentes web son las propiedades de los elementos HTML. En lugar de codificar el contenido de texto del encabezado de nuestra ventana, podemos convertirlo en una entrada de propiedad en el elemento. Podemos usar Lit para hacer que nuestras propiedades sean reactivas , lo que activa los métodos del ciclo de vida cuando se modifican.

Para hacer esto, necesitamos hacer tres cosas:

  1. Definir las propiedades reactivas,
  2. Asigne un valor predeterminado,
  3. Representa el valor de la propiedad reactiva al DOM.

En primer lugar, debemos especificar las propiedades reactivas que queremos habilitar para nuestro componente:

class A2kWindow extends LitElement { static styles = css`...`; static properties = { heading: {}, }; render() {...}}

Haremos esto especificando el propertiesobjeto estático en nuestra clase. Luego especificamos los nombres de las propiedades que queremos, junto con algunas opciones pasadas como objeto. Las opciones predeterminadas de Lit manejan la conversión de propiedades de cadena de forma predeterminada. Esto significa que no necesitamos aplicar ninguna opción y podemos dejarlo headingcomo un objeto vacío.

Nuestro siguiente paso es asignar un valor predeterminado. Haremos esto dentro del método constructor del componente.

class A2kWindow extends LitElement { static styles = css`...`; static properties = {...}; constructor() { super(); this.heading = "Building Retro Web Components with Lit"; } render() {...}}

Nota: ¡ No olvides llamar super()!

Y finalmente, agreguemos un poco más de marcado y representemos el valor en el DOM:

 

class A2kWindow extends LitElement { static styles = css`...`; static properties = {...}; constructor() {...} render() { return html` div div div p${this.heading}/p /div slot/slot /div /div `; }}

Una vez hecho esto, volvamos a nuestro navegador y veamos cómo se ve todo:

( Vista previa grande )

¡Muy convincente!

Prima

Aplique un encabezado personalizado al a2k-elementarchivo index.html.

Breve respiro ‍

¡Es maravilloso ver con qué facilidad podemos crear una interfaz de usuario de 1998 con primitivas modernas en 2022!

Diseño de una interfaz Windows, en un navegador de Google, dentro de un sistema operativo Apple. ( Vista previa grande )

¡Y aún no hemos llegado a las partes divertidas! En las siguientes secciones, veremos el uso de algunos de los conceptos intermedios de Lit para crear una funcionalidad de arrastre de una manera que sea reutilizable en todos los componentes personalizados.

Parte 2: Hacer que nuestro componente se pueda arrastrar

¡Aquí es donde las cosas se ponen un poco complicadas! Nos estamos moviendo hacia un territorio de literatura intermedia, así que no te preocupes si no todo tiene perfecto sentido.

Antes de comenzar a escribir el código, hagamos un resumen rápido de los conceptos con los que jugaremos.

Directivas

Como has visto, al escribir nuestras plantillas HTML en Lit, las escribimos dentro de la htmletiqueta literals. Esto nos permite utilizar JavaScript para alterar el comportamiento de nuestras plantillas. Podemos hacer cosas como evaluar expresiones:

html`p${this.heading}/p`

Podemos devolver plantillas específicas bajo ciertas condiciones:

html`p${this.heading ? this.heading : “Please enter a heading”}/p`

Habrá momentos en los que necesitaremos salir del flujo de renderizado normal del sistema de renderizado de Lit. Es posible que quieras renderizar algo más adelante o ampliar la funcionalidad de la plantilla de Lit. Esto se puede lograr mediante el uso de directivas. Lit tiene un puñado de directivas integradas.

Usaremos la styleMapdirectiva, que nos permite aplicar estilos directamente a un elemento a través de un objeto JavaScript. Luego, el objeto se transforma en los estilos en línea del elemento. Esto será útil cuando ajustemos la posición del elemento de nuestra ventana, ya que la posición del elemento es administrada por las propiedades CSS. En resumen, styleMapgira:

const top = this.top // a variable we could get from our class, a function, or anywherestyleMap({ position: "absolute", left: "100px", top})

en

"position: absolute; top: 50px; left: 100px;"

El uso styleMapfacilita el uso de variables para cambiar estilos.

Controladores

Lit tiene varias formas útiles de componer componentes complejos a partir de fragmentos de código más pequeños y reutilizables. Nails Trends

Una forma es construir componentes a partir de muchos componentes más pequeños. Por ejemplo, un botón de icono que se parece a este:

 

( Vista previa grande )

El marcado puede tener el siguiente marcado:

class IconButton extends LitElement { render() { return html` a2k-button a2k-icon icon="windows-icon"/a2k-icon slot/slot /a2k-button ` }}

En el ejemplo anterior, estamos componiendo nuestro IconButtona partir de dos componentes web preexistentes.

Otra forma de componer lógica compleja es encapsular estados y comportamientos específicos en una clase. Hacerlo nos permite desacoplar comportamientos específicos de nuestro marcado. Esto se puede hacer mediante el uso de controladores, una forma entre marcos de compartir lógica que puede desencadenar re-renderizaciones en un componente. También tienen la ventaja de conectarse con el ciclo de vida del componente.

Nota: Dado que los controladores son multiframe, se pueden usar en React y Vue con adaptadores pequeños.

Con los controladores, podemos hacer algunas cosas interesantes, como gestionar el estado de arrastre y la posición de su componente anfitrión. Curiosamente, ¡eso es exactamente lo que planeamos hacer!

Si bien un controlador puede parecer complicado, si analizamos su esqueleto, podremos entender qué es y qué hace.

export class DragController { x = 0; y = 0; state = "idle" styles = {...} constructor(host, options) { this.host = host; this.host.addController(this); } hostDisconnected() {...} onDragStart = (pointer, ev) = {...}; onDrag = (_, pointers) = {...};}

Comenzamos inicializando nuestro controlador registrándolo con el componente host y almacenando una referencia al host. En nuestro caso, el elemento anfitrión será nuestro a2k-windowcomponente.

Una vez que hayamos hecho eso, podemos conectarnos a los métodos del ciclo de vida de nuestro host, como hostConnected, hostUpdate, hostUpdated, hostDisconnectedetc., para ejecutar una lógica específica de arrastre. En nuestro caso, sólo necesitaremos conectarnos hostDisconnectedpara fines de limpieza.

Finalmente, podemos agregar nuestros propios métodos y propiedades a nuestro controlador que estarán disponibles para nuestro componente host. Aquí definimos algunos métodos privados que serán llamados durante las acciones de arrastre. También estamos definiendo algunas propiedades a las que nuestro elemento host puede acceder.

Cuando se invocan las funciones onDragy onDragStart, actualizamos nuestra stylespropiedad y solicitamos que nuestro componente host se vuelva a representar. Dado que nuestro componente anfitrión convierte este objeto de estilo en CSS en línea (a través de la styleMapdirectiva), nuestro componente aplicará los nuevos estilos.

Si esto suena complicado, es de esperar que este diagrama de flujo visualice mejor el proceso.

( Vista previa grande )

Escribiendo nuestro controlador

Posiblemente la parte más técnica del artículo: ¡conectemos nuestro controlador!

Comencemos completando la lógica de inicialización de nuestro controlador:

 

export class DragController { x = 0; y = 0; state = "idle"; styles = { position: "absolute", top: "0px", left: "0px", }; constructor(host, options) { const { getContainerEl = () = null, getDraggableEl = () = Promise.resolve(null), } = options; this.host = host; this.host.addController(this); this.getContainerEl = getContainerEl; getDraggableEl().then((el) = { if (!el) return; this.draggableEl = el; this.init(); }); } init() {...} hostDisconnected() {...} onDragStart = (pointer) = {...}; onDrag = (_, pointers) = {...};}

La principal diferencia entre este fragmento y el esqueleto anterior es la adición del argumento de opciones. Permitimos que nuestro elemento anfitrión proporcione devoluciones de llamada que nos den acceso a dos elementos diferentes: el contenedor y el elemento arrastrable. Usaremos estos elementos más adelante para calcular los estilos de posición correctos.

Por razones que abordaré más adelante, getDraggableEles una promesa que devuelve el elemento arrastrable. Una vez que se resuelve la promesa, almacenamos el elemento en la instancia del controlador y activaremos la función de inicialización, que adjunta los detectores de eventos de arrastre al elemento arrastrable.

init() { this.pointerTracker = new PointerTracker(this.draggableEl, { start: (...args) = { this.onDragStart(...args); this.state = "dragging"; this.host.requestUpdate(); return true; }, move: (...args) = { this.onDrag(...args); }, end: (...args) = { this.state = "idle"; this.host.requestUpdate(); }, });}

Usaremos la PointerTrackerbiblioteca para rastrear eventos de puntero fácilmente. Es mucho más agradable usar esta biblioteca que escribir la lógica del modo de entrada cruzada en varios navegadores para admitir eventos de puntero.

PointerTrackerrequiere dos argumentos, draggableEly un objeto de funciones que actúan como controladores de eventos para los eventos de arrastre:

  • start: se invoca cuando se presiona el puntero draggableEl;
  • move: se invoca al arrastrar draggableEl;
  • end: se invoca cuando soltamos el puntero de draggableEl.

Para cada uno, actualizamos el método de arrastrar state, invocamos la devolución de llamada de nuestro controlador o ambas cosas. Nuestro elemento host utilizará la statepropiedad como atributo de elemento, por lo que activamos this.host.requestUpdatepara garantizar que el host se vuelva a representar.

Al igual que con draggableEl, asignamos una referencia a la pointerTrackerinstancia a nuestro controlador para usarla más adelante.

A continuación, comencemos a agregar lógica a las funciones de la clase. Empezaremos con la onDragStartfunción:

onDragStart = (pointer, ev) = { this.cursorPositionX = Math.floor(pointer.pageX); this.cursorPositionY = Math.floor(pointer.pageY);};

Aquí almacenamos la posición actual del cursor, que usaremos en la onDragfunción.

onDrag = (_, pointers) = { this.calculateWindowPosition(pointers[0]);};

Cuando onDragse llama a la función, se proporciona una lista de los punteros activos. Dado que solo admitiremos el arrastre de una ventana a la vez, podemos acceder de forma segura al primer elemento de la matriz. Luego lo enviaremos a una función que determina la nueva posición del elemento. Abróchate el cinturón porque es un poco salvaje:

 

calculateWindowPosition(pointer) { const el = this.draggableEl; const containerEl = this.getContainerEl(); if (!el || !containerEl) return; const oldX = this.x; const oldY = this.y; //JavaScript’s floats can be weird, so we’re flooring these to integers. const parsedTop = Math.floor(pointer.pageX); const parsedLeft = Math.floor(pointer.pageY); //JavaScript’s floats can be weird, so we’re flooring these to integers. const cursorPositionX = Math.floor(pointer.pageX); const cursorPositionY = Math.floor(pointer.pageY); const hasCursorMoved = cursorPositionX !== this.cursorPositionX || cursorPositionY !== this.cursorPositionY; // We only need to calculate the window position if the cursor position has changed. if (hasCursorMoved) { const { bottom, height } = el.getBoundingClientRect(); const { right, width } = containerEl.getBoundingClientRect(); // The difference between the cursor’s previous position and its current position. const xDelta = cursorPositionX - this.cursorPositionX; const yDelta = cursorPositionY - this.cursorPositionY; // The happy path - if the element doesn’t attempt to go beyond the browser’s boundaries. this.x = oldX + xDelta; this.y = oldY + yDelta; const outOfBoundsTop = this.y 0; const outOfBoundsLeft = this.x 0; const outOfBoundsBottom = bottom + yDelta window.innerHeight; const outOfBoundsRight = right + xDelta = window.innerWidth; const isOutOfBounds = outOfBoundsBottom || outOfBoundsLeft || outOfBoundsRight || outOfBoundsTop; // Set the cursor positions for the next time this function is invoked. this.cursorPositionX = cursorPositionX; this.cursorPositionY = cursorPositionY; // Otherwise, we force the window to remain within the browser window. if (outOfBoundsTop) { this.y = 0; } else if (outOfBoundsLeft) { this.x = 0; } else if (outOfBoundsBottom) { this.y = window.innerHeight - height; } else if (outOfBoundsRight) { this.x = Math.floor(window.innerWidth - width); } this.updateElPosition(); // We trigger a lifecycle update. this.host.requestUpdate(); }}updateElPosition(x, y) { this.styles.transform = `translate(${this.x}px, ${this.y}px)`;}

Ciertamente no es el código más bonito, así que hice todo lo posible para anotar el código para aclarar lo que está pasando.

Para resumir:

  • Cuando se invoca la función, verificamos que tanto draggableEly containerElestén disponibles.
  • Luego accedemos a la posición del elemento y a la posición del cursor.
  • Luego calculamos si el cursor se movió. Si no es así, no hacemos nada.
  • Establecemos la nueva xposición ydel elemento.
  • Determinamos si el elemento intenta o no romper los límites de la ventana.
    • Si es así, actualizamos la posición xo ypara devolver el elemento a los límites de la ventana.
  • Actualizamos this.stylescon las novedades xy yvalores.
  • Luego activamos la función de ciclo de vida de actualización del host, lo que hace que nuestro elemento aplique los estilos.

Revise la función varias veces para asegurarse de estar seguro de lo que hace. Están sucediendo muchas cosas, así que no te preocupes si no se absorbe de inmediato.

 

La updateElPositionfunción es una pequeña ayuda en la clase para aplicar los estilos a la stylespropiedad.

También necesitamos agregar un poco de limpieza para asegurarnos de que dejemos de rastrear si nuestro componente se desconecta mientras lo arrastramos.

hostDisconnected() { if (this.pointerTracker) { this.pointerTracker.stop(); }}

Finalmente, debemos regresar a nuestro a2k-window.jsarchivo y hacer tres cosas:

  • inicializar el controlador,
  • aplicar los estilos de posición,
  • rastrear el estado de arrastre.

Así es como se ven estos cambios:

class A2kWindow extends LitElement { static styles = css`...`; static properties = {...}; constructor() {...} drag = new DragController(this, { getContainerEl: () = this.shadowRoot.querySelector("#window"), getDraggableEl: () = this.getDraggableEl(), }); async getDraggableEl() { await this.updateComplete; return this.shadowRoot.querySelector("#draggable"); } render() { return html` div style=${styleMap(this.drag.styles)} div div data-dragging=${this.drag.state} p${this.heading}/p /div slot/slot /div /div `; }}

Estamos usando this.shadowRoot.querySelector(selector)para consultar nuestro DOM oculto. Esto nos permite al controlador acceder a elementos DOM a través de límites DOM ocultos.

Debido a que planeamos enviar eventos desde nuestro elemento de arrastre, debemos esperar hasta que se haya completado la representación, de ahí la await this.updateCompletedeclaración.

Once this is all completed, you should be able to jump back into the browser and drag your component around, like so:

Part 3: Creating The Broken Window Effect

Our component is pretty self-contained, which is great. We could use this window element anywhere on our site and drag it without writing any additional code.

And since we’ve created a reusable controller to handle all of the drag functionality, we can add that behavior to future components like a desktop icon.

Now let’s start building out that cool broken window effect when we drag our component.

We could bake this behavior into the window element itself, but it’s not really useful outside of a specific use case, i.e., making a cool visual effect. Instead, we can get our drag controller to emit an event whenever the onDrag callback is invoked. This means that anyone using our component can listen to the drag event and do whatever they want.

To create the broken window effect, we’ll need to do two things:

  • dispatch and listen to the drag event;
  • add the broken window element to the DOM.

Dispatching and listening to events in Lit

Lit has a handful of different ways to handle events. You can add event listeners directly within your templates, like so:

handleClick() { console.log("Clicked");}render() { html`button @click="${this.handleClick}"Click me!/button`}

We’re defining the function that we want to fire on button click and passing it through to the element which will be invoked on click. This is a perfectly viable option, and it’s the approach I’d use if the element and callback are located close together.

As I mentioned earlier, we won’t be baking the broken window behavior into the component, as passing down event handlers through a number of different web components would become cumbersome. Instead, we can leverage the native window event object to have a component dispatch an event and have any of its ancestors listen and respond. Have a look at the following example:

// Event Listenerclass SpecialListener extends LitElement { constructor() { super() this.specialLevel = ''; this.addEventListener('special-click', this.handleSpecialClick) } handleSpecialClick(e) { this.specialLevel = e.detail.specialLevel; } render() { html`div p${this.specialLevel}/p special-button /div` }}// Event Dispatcherclass SpecialButton extends LitElement { handleClick() { const event = new CustomEvent("special-click", { bubbles: true, composed: true, detail: { specialLevel: 'high', }, }); this.dispatchEvent(event); } render() { html`button @click="${this.handleClick}"Click me!/button` }}

Note: Don’t forget to check out the MDN resources if you need a refresher on native DOM Events.

We have two components, a listener and a dispatcher. The listener is a component that adds an event listener to itself. It listens to the special-click event and outputs the value the event sends through.

Our second component, SpecialButton, is a descendant of SpecialListener






Tal vez te puede interesar:

  1. Creación de su propia biblioteca de validación de React: las características (Parte 2)
  2. Creación de un componente de diagrama de Gantt interactivo con Vanilla JavaScript (Parte 1)
  3. Introducción a Quasar Framework: creación de aplicaciones multiplataforma
  4. Creación y acoplamiento de una aplicación Node.js con arquitectura sin estado con la ayuda de Kinsta

Creación de un componente web retro que se puede arrastrar con iluminación

Creación de un componente web retro que se puede arrastrar con iluminación

Índice Empezando index.html

programar

es

https://aprendeprogramando.es/static/images/programar-creacion-de-un-componente-web-retro-que-se-puede-arrastrar-con-iluminacion-1157-0.jpg

2024-04-04

 

Creación de un componente web retro que se puede arrastrar con iluminación
Creación de un componente web retro que se puede arrastrar con iluminación

Si crees que alguno de los contenidos (texto, imagenes o multimedia) en esta página infringe tus derechos relativos a propiedad intelectual, marcas registradas o cualquier otro de tus derechos, por favor ponte en contacto con nosotros en el mail [email protected] y retiraremos este contenido inmediatamente

 

 

Top 20