“Atention Is All You Need”

  • Efectivamente, falta una "t". Y ahora que prestáis atención, ya tengo lo que necesito para empezar...
  • Esa frase es el título de un famoso Paper que, para el que no lo sepa, dió orígen a la actual revolución de IA.

SmarkForm

  • ...Pero hoy no vamos a hablar de IA.
  • Hoy vamos a desinfoxificarnos (o "desiaficarnos") de tanta IA.
  • Hoy hablaremos de Formularios Web.

Czy jest na sali jakiś Polak?

  • Traducción: «Algún polonés en la sala?»
  • Pequeña broma para comentar que recientemente descubrí que "Smark" significa "moco" en Polaco.

Smark = Smart + Markup

🥱

 

Otra libreria de formularios...

 

🤔

¿Por qué SmarkForm es distinta?

👌 Sencillez

👉 Zero-Wiring

👌 Sencillez 👀

🗒️ HTML
🎨 CSS
⚙️ JS
👁️ Preview
📝 Notes
<div id="myForm">
    <p class="row">
        <label data-smark>Nombre:</label>
        <input data-smark='{"name":"name", "type":"input"}' type="text" />
        <button data-smark='{"action":"clear","context":"name"}'></button>
    </p>
    <p class="row">
        <label data-smark>Teléfono:</label>
        <input data-smark name="phone" type="tel" />
        <button data-smark='{"action":"clear","context":"phone"}'></button>
    </p>
    <p class="row">
        <label data-smark>eMail:</label>
        <input data-smark name="email" type="email" />
        <button data-smark='{"action":"clear","context":"email"}'></button>
    </p>
    <p class="row">
        <button data-smark='{"action":"clear"}'>❌ Borrar</button>
        <button data-smark='{"action":"export"}'>💾 Guardar</button>
    </p>
</div>

.row {
    padding: .5em;
}

const myForm = new SmarkForm(
    document.getElementById("myForm")
);

  • 👉 Los elementos sin la etiqueta data-smark no son tenidos en cuenta.
  • ➡️ Podríamos incluso insertar tags <input> para otros widgets.
  • 📝 Los que sí la tienen son TODOS componentes del formulario y tienen una propiedad type que indica su tipo. Aunque en la mayoría de los casos éste es implícito y puede omitirse...
  • 👉 El HTML original es funcional y puede ser trabajado por un diseñador sin interfréncias con el código.
  • 👉 Las etiquetas (<label> funcionan sin necesidad de asignar y mapear manualmente identificadores para cada campo.
  • 👉 Los botones con la propiedad action son (implícitamente) componentes de tipo "trigger" que disparan acciones de otro componente (contexto).
  • ➡️ El contexto es, implícitamente, de entre sus componentes ancestros, el más cercano que implemente la acción especificada.
  • ➡️ El contexto puede alterarse mediante la propiedad context del trigger, especificando una ruta absoluta (empezando por "/") o relativa (desde el componente padre del trigger).
  • ➡️ Las rutas se construyen utilizando la propiedad "name" de los componentes, separando por "/" y pudiendo utilizar el comodín ".." para subir de nivel.

🛠️ DIY

1. Crea tu primer formulario

 

⚡ Potencia

⚡ Potencia 👀

🗒️ HTML
🎨 CSS
⚙️ JS
👁️ Preview
📝 Notes
<div id="myForm">
    <p class="row">
        <label data-smark>Nombre:</label><input data-smark='{"name":"name"}' type="text">
        <label data-smark>Edad:</label><input data-smark='{"name":"age"}' type="number">
    </p>
    <fieldset class="row" data-smark='{"name":"conatact_data"}'>
        <button data-smark='{"action":"removeItem", "context":"phones", "target":"*", "keep_non_empty":true}' title='Limpiar vacíos'>🧹</button>
        <button data-smark='{"action":"removeItem", "context":"phones", "keep_non_empty":true}' title='Eliminar teléfono'></button>
        <button data-smark='{"action":"addItem","context":"phones"}' title='Añadir teléfono'></button>
        <label data-smark>Teléfonos:</label>
        <ul data-smark='{"name": "phones", "of": "input", "sortable":true, "min_items":0, "max_items":5, "exportEmpties": true}'>
            <li data-smark='{"role": "empty_list"}' class="row">(No dispone)</li>
            <li class="row">
                <label data-smark>📞 </label><input type="tel" data-smark>
                <button data-smark='{"action":"removeItem"}' title='Eliminar éste teléfono'></button>
            </li>
        </ul>
        <p class="row"><label data-smark>eMail:</label>
        <input type="email" name="email" data-smark /></p>
    </fieldset>
    <p class="row">
        <button data-smark='{"action":"clear"}'>❌ Borrar</button>
        <button data-smark='{"action":"import"}'>📂 Abrir</button>
        <button data-smark='{"action":"export"}'>💾 Guardar</button>
    </p>
</div>

.row {
    padding: .5em;
}

fieldset.row {
    border: solid 1px;
    margin: 3px;
}

button:disabled {
    opacity: .5;
}

const myForm = new SmarkForm(
    document.getElementById("myForm")
);

/* Do something on data export */
myForm.on("AfterAction_export", ({data})=>{
    window.alert(JSON.stringify(data, null, 4));
});

/* Get data from somewhere on import */
myForm.on("BeforeAction_import", async (ev)=>{
    let data = window.prompt('Fill JSON data');
    if (data === null) return void ev.preventDefault(); /* Cancelled */
    try {
        ev.data = JSON.parse(data);
    } catch(err) {
        alert(err.message);
        ev.preventDefault();
    };
});

/* Ask for confirmation unless form is already empty: */
myForm.on("BeforeAction_clear", async ({context, preventDefault}) => {
    if (
        ! await context.isEmpty()        /* Form (or field) is not empty */
        && ! confirm("Descartar datos?") /* User clicked the "Cancel" button. */
    ) {
        /* Prevent default (clear form) behaviour: */
        preventDefault();
    };
});

  • (No dispone)

  • 👉 Interceptando los eventos adecuados, podemos, por ejemplo:
  • ➡️ Inyectar o capturar los datos (JSON) de las acciones import y export.
  • ➡️ Alterar el comportamiento de la acción clear para que nos solicite confirmación cuando sea pertinente.
  • 👉 Para agrupar los datos de contacto, usamos un campo de tipo form que devuelve JSON.
  • 👉 En lugar de un sólo teléfono hemos utilizado una lista de longitud variable.
  • 📝 Los descendientes directos de las listas son plantillas que cumplen un determinado rol. Por defecto "list_item" que es obligatorio y se utilizará para renderizar los elementos de la lista. Pero hay otros, como empty_list, que nos ha permitido mostrar el texto "(No dispone)" cuando la lista esté vacía.
  • ➡️ La propiedad of nos permite ahorrarnos el atributo data-smark en la plantilla list_item si sólo es para especficar el tipo. (📌 El tipo input activa el patrón Singleton).
  • ➡️ Con min_items=0 y max_items=5, respectivamente, permitimos que la lista esté vacía y limitamos su longitud a un máximo de 5 elementos.
  • ➡️ Las propiedades sortable y export_empties permiten, a su vez, que el usuario pueda ordenar la lista arrastrando los elementos y que los que estén vacíos también se exporten.

🛠️ DIY

2. Completa tu formulario

 

🫶 Usabilidad

🫶 Usabilidad 👀

🗒️ HTML
🎨 CSS
⚙️ JS
👁️ Preview
📝 Notes
<div id="myForm">
    <button data-smark='{"action":"addItem","context":"employee","hotkey":"+"}' title='Nuevo empleado'>👥</button>
    <label data-smark>Empleados:</label>
    <div data-smark='{"type":"list","name":"employee", "min_items":0,"sortable":true}'>
        <fieldset class="row">
            <p class="row">
                <label>Empleado</label>
                <span data-smark='{"action":"position"}'></span>/<span data-smark='{"action":"count"}'></span>:
                <input data-smark='{"name":"name"}' type="text" placeholder='Nombre...'>
                <button data-smark='{"action":"removeItem","hotkey":"-"}' style='float: right;' title='Baja empleado'>🔥</button>
            </p>
            <label data-smark>📞 Teléfonos:</label>
            <ul data-smark='{"name": "phones", "of": "input", "sortable":true, "max_items":3}'>
                <li class="row">
                    <button data-smark='{"action":"removeItem","hotkey":"-"}' title='Eliminar Teléfono'></button>
                    <input type="tel" data-smark />
                    <button data-smark='{"action":"addItem","hotkey":"+"}' title='Añadir Teléfono'></button>
                </li>
            </ul>
        </fieldset>
    </div>
    <p class="row">
        <button data-smark='{"action":"clear","hotkey":"x"}'>❌ Borrar</button>
        <button data-smark='{"action":"export","hotkey":"s"}'>💾 Guardar</button>
    </p>
</div>

button[data-hotkey] {
    position: relative;
    overflow-x: display;
}
button[data-hotkey]::before {
    content: attr(data-hotkey);
    display: inline-block;
    position: absolute;
    top: 2px;
    left: 2px;
    z-index: 10;
    background-color: #ffd;
    padding: 2px 8px;
    border-radius: 4px;
    font-weight: bold;
    font-family: sans-serif;
    font-size: .8em;
    white-space: nowrap;
    transform: scale(1.8) translate(.1em, .1em);
}

.row {
    padding: .5em;
}

fieldset.row {
    border: solid 1px;
    margin: 3px;
}

button:disabled {
    opacity: .5;
}

const myForm = new SmarkForm(
    document.getElementById("myForm")
);

/* Do something on data export */
myForm.on("AfterAction_export", ({data})=>{
    window.alert(JSON.stringify(data, null, 4));
});

/* Get data from somewhere on import */
myForm.on("BeforeAction_import", async (ev)=>{
    let data = window.prompt('Fill JSON data');
    if (data === null) return void ev.preventDefault(); /* Cancelled */
    try {
        ev.data = JSON.parse(data);
    } catch(err) {
        alert(err.message);
        ev.preventDefault();
    };
});

/* Ask for confirmation unless form is already empty: */
myForm.on("BeforeAction_clear", async ({context, preventDefault}) => {
    if (
        ! await context.isEmpty()        /* Form (or field) is not empty */
        && ! confirm("Descartar datos?") /* User clicked the "Cancel" button. */
    ) {
        /* Prevent default (clear form) behaviour: */
        preventDefault();
    };
});

/:

  • 👉 Las teclas rápidas se revelan al pulsar la tecla Ctrl y se activan combinándolas con ésta.
  • ➡️ La revelación, por defecto, se hace mediante el atributo data-hotkey aunque éste comportamiento podrá alterarse mediante la intercepción del evento.
  • ➡️ De éste modo, la forma en que se muestre al usuario dicha "revelación" (pista) depende totalmente del CSS.
  • ➡️ Si hay más de un trigger con la misma tecla rápida, se activará sólo la del que esté contextualmente mas cerca al foco.
  • ➡️ Si un trigger está desactivado, su tecla rápida no se revela pero, por consistencia, tampoco "cede el paso".
  • 👉 Los controles (triggers) para añadir o quitar elementos de una lista que estén integrados déntro de éstos, no reciben el foco al navegar con tabulador siempre que dispongan de tecla rápida.
  • 👉 Éste ejemplo tiene dos listas anidadas que podemos reordenar simplemente arrastrando con el ratón (y en el futuro también automáticamente según criterio)...

⚠️ El software utilizado para ésta presentación interfiere en los eventos de teclado y ratón. Para una mejor apreciación, ver los ejemplos del Manual de SmarkForm:

🔗 (https://smarkform.bitifet.net).

🛠️ DIY

3. Mejora la Usabilidad

 

♿ Accesibilidad

🏗️ Extendibilidad

🚁 Independencia

🔮 Futuro

➕ ❓

Cómo puedo ayudar?

Thanks for your attention!!

Grácias

TechSpirit.org and Mallorca Software Crafters FRS Systems

Preguntas... ??

Use a spacebar or arrow keys to navigate.
Press 'P' to launch speaker console.