bc-formio-template-extend
Version:
Extension of Formio JS's template for formio components following web accessibility
177 lines (166 loc) • 5.69 kB
text/typescript
/** @format */
import { RadioComponentConfig } from '../types/RadioComponentConfig';
export class Radio {
private Templates: typeof window.Formio.Templates;
constructor() {
this.Templates = window.Formio.Templates || {};
}
radioStyle(): string {
return `
.bc-radios, .bc-radios__item {
position: relative;
}
.bc-radios {
border: none !important;
padding: 0 !important;
}
.bc-radios__item {
display: flex;
flex-wrap: wrap;
margin-bottom: 10px;
}
.bc-radios__input {
z-index: 1;
width: 44px;
height: 44px;
margin: 0 !important;
opacity: 0;
left: 0;
top: 0;
cursor: pointer;
}
.bc-radio__label {
align-self: center;
max-width: calc(100% - 74px);
margin-bottom: 0;
margin-left: 40px;
padding: 7px 15px;
cursor: pointer;
touch-action: manipulation;
}
.bc-radio__label > .bc_checkedItem:before {
content: "";
box-sizing: border-box;
position: absolute;
top: 2px;
left: 2px;
width: 40px;
height: 40px;
border: 2px solid;
border-radius: 50%;
background: transparent;
}
.bc-radio__label > .bc_checkedItem:after {
content: "";
position: absolute;
top: 12px;
left: 12px;
width: 0;
height: 0;
border: 10px solid;
border-radius: 50%;
opacity: 0;
background: #000;
}
.bc-radios__input:checked + .bc_checkedItem:after {
opacity: 1;
}
.bc-radios__input:checked + .bc_checkedItem:before,
.bc-radios__input[type=radio]:focus + .bc_checkedItem:before {
outline: 3px solid rgba(0, 0, 0, 0) !important;
outline-offset: 1px;
box-shadow: 0 0 0 3px #fd0 !important;
border-width: 4px;
}`;
}
radio(): { form: (ctx: RadioComponentConfig) => string } {
return {
form: (ctx: RadioComponentConfig): string => {
return `
<fieldset
class="govuk-radios bc-radios ${ctx.inline ? 'govuk-radios--inline' : ''}"
ref="radioGroup"
role="${ctx.component.type === 'selectboxes' ? 'group' : 'radiogroup'}"
aria-required="${ctx.component.validate?.required}"
aria-labelledby="${ctx.instance.id}-${ctx.component.key}--legend"
${
ctx.component.description
? `aria-describedby="d-${ctx.instance.id}-${ctx.component.key}"`
: ''
}
>
<legend hidden id="${ctx.instance.id}-${ctx.component.key}--legend">${
ctx.component.label
}</legend>
${ctx.values
.map((item, index) => {
const inputId = `${ctx.instance?.root?.id}-${ctx.id}-${ctx.row}-${
typeof item.value === 'object' ? item.value + '-' + index : item.value
}`;
const isChecked =
ctx.value &&
(ctx.value === item.value ||
(typeof ctx.value === 'object' &&
Object.prototype.hasOwnProperty.call(ctx.value, item.value) &&
ctx.value[item.value]));
return `
<div class="bc-radios__item ${ctx.input.attr.type} form-check${
ctx.inline ? '-inline' : ''
}" ref="wrapper">
<label class="bc-radio__label label-position-${
ctx.component.optionsLabelPosition
}" for="${inputId}">
${
['left', 'top'].includes(ctx.component.optionsLabelPosition)
? `<span>${ctx.t(item.label, { _userInput: true })}</span>`
: ''
}
<input
ref="input"
class="bc-radios__input${ctx.input.attr.class ? ' ' + ctx.input.attr.class : ''}"
${Object.entries(ctx.input.attr)
.filter(([key]) => key !== 'class')
.map(([key, val]) => `${key}="${val}"`)
.join(' ')}
value="${item.value}"
${isChecked ? 'checked' : ''}
${item.disabled ? 'disabled' : ''}
id="${inputId}"
role="${ctx.component.type === 'selectboxes' ? 'checkbox' : 'radio'}"
/>
<span class="bc_checkedItem"></span>
${
!ctx.component.optionsLabelPosition ||
['right', 'bottom'].includes(ctx.component.optionsLabelPosition)
? `<span>${ctx.t(item.label, { _userInput: true })}</span>`
: ''
}
</label>
</div>`;
})
.join('')}
</fieldset>`;
},
};
}
componentStyles(): string[] {
return [this.radioStyle()];
}
styleTagContent(): string {
return this.componentStyles().join('\n');
}
injectStyleToHead(): void {
if (!document.getElementById('custom-bc-template__style')) {
const styleTag = document.createElement('style');
styleTag.id = 'custom-bc-template__style';
styleTag.textContent = this.styleTagContent();
document.head.appendChild(styleTag);
}
}
init(): void {
this.Templates.current = {
radio: this.radio(),
};
this.injectStyleToHead();
}
}