Override @formio/js Templates

by brendanbond

HTML

<link
  href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"
  rel="stylesheet"
  integrity="sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB"
  crossorigin="anonymous"
/>
<link rel="stylesheet" href="https://cdn.form.io/js/formio.full.min.css" />

<div class="container">
  <div id="formio"></div>
</div>
<script src="https://cdn.form.io/js/formio.full.js"></script>

CSS

.radio-block label {
  background: #228b22;
  border: 3px solid transparent;
  cursor: pointer;
}
.radio-block.radio-selected label {
  border-color: black;
}

@keyframes slideInFromRight {
  from {
    transform: translateX(100%);
    opacity: 0;
  }
  to {
    transform: translateX(0);
    opacity: 1;
  }
}
@keyframes slideInFromLeft {
  from {
    transform: translateX(-100%);
    opacity: 0;
  }
  to {
    transform: translateX(0);
    opacity: 1;
  }
}
.wizard-page.slide-left {
  animation: slideInFromRight 0.4s ease-out;
}
.wizard-page.slide-right {
  animation: slideInFromLeft 0.4s ease-out;
}

JavaScript

// Here we override our radio button and wizard templates. Again, for a full design system it's better to keep these version controlled, but hopefully this demonstrates the concepts. See https://help.form.io/developers/form-development/form-templates#what-is-a-template
// I also used our open source bootstrap templates for guidance, found here: https://github.com/formio/bootstrap/tree/main/src/templates/bootstrap5
Formio.Templates.current = {
  wizard: {
    form: (() => {
      let lastPage = -1;
        let direction = "";
      return (ctx) => {
        if (lastPage !== -1 && ctx.currentPage !== lastPage) {
          direction = ctx.currentPage > lastPage ? "slide-left" : "slide-right";
          setTimeout(() => { direction = ''; }, 400);
        }
        lastPage = ctx.currentPage;
        return `
          <div class="${ctx.className}">
            <div style="position: relative; overflow: hidden;">
              ${ctx.wizardHeader || ""}
              <div class="wizard-page ${direction}" ref="${ctx.wizardKey}">
                ${ctx.components}
              </div>
              ${ctx.wizardNav || ""}
            </div>
          </div>`;
      };
    })(),
  },
  radio: {
    form: (ctx) => `
      <div class="d-flex flex-column gap-2" ref="radioGroup" role="radiogroup">
        ${ctx.values
          .map((item, index) => {
            const id = `${ctx.instance.root?.id}-${ctx.id}-${ctx.row}-${typeof item.value === "object" ? item.value + "-" + index : item.value}`;
            return `
            <div class="radio-block" ref="wrapper">
              <input ref="input" type="radio" class="position-absolute opacity-0"
                name="${ctx.input.attr.name}" value="${item.value}"
                ${item.disabled ? "disabled" : ""}
                id="${id}" role="radio">
              <label class="d-block w-100 p-3 text-white text-center rounded fw-medium"
                for="${id}">
               ...