diff --git a/blocks/form/form.css b/blocks/form/form.css index 3e88b9e..69428f6 100644 --- a/blocks/form/form.css +++ b/blocks/form/form.css @@ -75,40 +75,109 @@ } /* ===================================================================== FORMS GENERAL */ -.section-ready-to-fly form { +form { display: flex; flex-direction: column; gap: 52px; } -.section-ready-to-fly .field-wrapper { +fieldset { + max-width: none !important; /* Override hs styling */ +} + +.input { + margin-right: 0 !important; /* Override hs styling */ +} + +:is(.hs-fieldtype-text, .hs-fieldtype-phonenumber, .hs-fieldtype-textarea) .hs-input { + width: 100% !important; /* Override hs styling */ +} + +.form-columns-2 { + display: grid; + grid-template-columns: 1fr; + column-gap: 16px; + row-gap: 52px; +} + +@media (min-width: 700px) { + .form-columns-2 { + grid-template-columns: repeat(2, 1fr); + } +} + +.form-columns-2 .hs-form-field { + width: 100% !important; /* Override hs styling */ +} + +.field { position: relative; } -.section-ready-to-fly .field-wrapper input, -.section-ready-to-fly .field-wrapper textarea { +.field input, +.field textarea { margin: 0; max-width: 100vw; } -.section-ready-to-fly .field-wrapper textarea { +.field textarea { border: none; padding: 16px 25px; border-radius: 15px; - background-color: var(--color-light-purple); + background-color: #382F6F; +} + +.hs-richtext { + font-size: var(--body-font-size-s); +} + +.hs-richtext:not(:last-child) { + margin-bottom: 16px; +} + +/* ===================================================================== ERROR */ +.hs-error-msgs { + font-size: var(--body-font-size-s); + width: 100%; + grid-column: span 2; + list-style: none; + padding-left: 0; + margin: 0; +} + +:is(.hs-fieldtype-text, .hs-fieldtype-phonenumber) .hs-error-msgs { + position: absolute; + left: 0; + right: 0; + top: calc(100% + 8px); +} + +.hs-error-msgs label { + --background-color: transparent; + + color: var(--error-color) !important; /* Override hs style */ +} + +.error, +.error + label { + --text-color: var(--error-color); + + color: var(--error-color); } /* ===================================================================== CHECKBOX */ -.section-ready-to-fly .form-checkbox-wrapper { - display: flex; +.hs-fieldtype-booleancheckbox { + display: grid; + grid-template-columns: 25px 1fr; gap: 30px; + margin-bottom: 16px !important; /* Override hs styling */ } -.section-ready-to-fly .form-checkbox-wrapper label { +.hs-fieldtype-booleancheckbox label { font-size: var(--body-font-size-s); } -.section-ready-to-fly .form-checkbox-wrapper input { +.hs-fieldtype-booleancheckbox input { --checkbox-size: 25px; position: relative; @@ -119,8 +188,8 @@ visibility: hidden; } -.section-ready-to-fly .form-checkbox-wrapper input::before, -.section-ready-to-fly .form-checkbox-wrapper input::after { +.hs-fieldtype-booleancheckbox input::before, +.hs-fieldtype-booleancheckbox input::after { box-sizing: border-box; content: ''; display: inline-block; @@ -132,53 +201,52 @@ border: 1px solid white; } -.section-ready-to-fly .form-checkbox-wrapper input::before { +.hs-fieldtype-booleancheckbox input::before { visibility: visible; } -.section-ready-to-fly .form-checkbox-wrapper input::after { +.hs-fieldtype-booleancheckbox input::after { transform: scale(60%, 60%); background-color: white; } -.section-ready-to-fly .form-checkbox-wrapper input:checked::after { +.hs-fieldtype-booleancheckbox input:checked::after { visibility: visible; } +.legal-consent-container .hs-form-booleancheckbox-display>span { + margin-left: 0 !important; /* Override hs styling */ + margin-top: -5px; /* Relativise the line height */ + margin-bottom: -5px; /* Relativise the line height */ +} + /* ===================================================================== TEXT AREAS */ -.section-ready-to-fly .form-text-area-wrapper { +.hs-fieldtype-textarea { display: flex; flex-direction: column; } -.section-ready-to-fly .form-text-area-wrapper label { +.hs-fieldtype-textarea label { margin-bottom: 5px; } -.section-ready-to-fly .form-text-area-wrapper textarea { +.hs-fieldtype-textarea textarea { height: 256px; background-color: #382F6F; } -.section-ready-to-fly .form-text-area-wrapper textarea:focus, -.section-ready-to-fly .form-text-area-wrapper textarea:not(:placeholder-shown){ +.hs-fieldtype-textarea textarea:focus, +.hs-fieldtype-textarea textarea:not(:placeholder-shown){ outline: none; } /* ===================================================================== TEXT FIELDS */ -.section-ready-to-fly .form-text-field-wrapper input { - border: 1px solid rgb(255 255 255 / 0%); - border-radius: 0; - border-bottom: 1px solid var(--text-color); - background-color: rgb(28 10 77 / 75%); -} - -.section-ready-to-fly .form-text-field-wrapper input:focus { +:is(.hs-fieldtype-text, .hs-fieldtype-phonenumber) input:focus { outline: none; border: 1px solid white; } -.section-ready-to-fly .form-text-field-wrapper label { +:is(.hs-fieldtype-text, .hs-fieldtype-phonenumber) label:not(.hs-error-msg) { position: absolute; top: 13px; transition: all 0.075s ease-out; @@ -186,20 +254,20 @@ pointer-events: none; } -.section-ready-to-fly .form-text-field-wrapper input:focus ~ label, -.section-ready-to-fly .form-text-field-wrapper input:not(:placeholder-shown) ~ label { +:is(.hs-fieldtype-text, .hs-fieldtype-phonenumber) input:focus ~ label:not(.hs-error-msg), +:is(.hs-fieldtype-text, .hs-fieldtype-phonenumber) input:not(:placeholder-shown) ~ label:not(.hs-error-msg) { transform-origin: left; padding: 5px; transform: translate(-5px, calc(-50% - 13px)) scale(0.8, 0.8); } /* ===================================================================== FORM SUBMIT BUTTON */ -.section-ready-to-fly .form-submit-wrapper { +.hs-submit .actions { display: flex; justify-content: right; } /* ===================================================================== HIDDEN */ -.section-ready-to-fly .hidden { +.hs-fieldtype-hidden { display: none; } diff --git a/blocks/form/form.js b/blocks/form/form.js index 0894e3f..f9a30a7 100644 --- a/blocks/form/form.js +++ b/blocks/form/form.js @@ -1,192 +1,41 @@ -function createSelect(fd) { - const select = document.createElement('select'); - select.id = fd.Field; - if (fd.Placeholder) { - const ph = document.createElement('option'); - ph.textContent = fd.Placeholder; - ph.setAttribute('selected', ''); - ph.setAttribute('disabled', ''); - select.append(ph); - } - fd.Options.split(',').forEach((o) => { - const option = document.createElement('option'); - option.textContent = o.trim(); - option.value = o.trim(); - select.append(option); - }); - if (fd.Mandatory === 'x') { - select.setAttribute('required', 'required'); - } - return select; -} - -function constructPayload(form) { - const payload = {}; - [...form.elements].forEach((fe) => { - if (fe.type === 'checkbox') { - if (fe.checked) payload[fe.id] = fe.value; - } else if (fe.id) { - payload[fe.id] = fe.value; - } - }); - return payload; -} - -async function submitForm(form) { - const payload = constructPayload(form); - const resp = await fetch(form.dataset.action, { - method: 'POST', - cache: 'no-cache', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ data: payload }), - }); - await resp.text(); - return payload; -} - -function createButton(fd) { - const button = document.createElement('button'); - button.textContent = fd.Label; - button.classList.add('button'); - if (fd.Type === 'submit') { - button.addEventListener('click', async (event) => { - const form = button.closest('form'); - const formBlock = button.closest('.form.block'); - if (form.checkValidity()) { - event.preventDefault(); - button.setAttribute('disabled', ''); - await submitForm(form); - const formThankYou = formBlock.querySelector('.form-thank-you'); - if (formThankYou) { - formBlock.querySelector('.form-content').classList.add('hidden'); - formThankYou.classList.remove('hidden'); - } - - window.location.hash = '#form'; +import { loadScript } from '../../scripts/lib-franklin.js'; +import { debounce } from '../../scripts/utilities.js'; + +function rearrangeInputLabels() { + const forms = document.querySelectorAll('form'); + forms.forEach((form) => { + const inputWrappers = form.querySelectorAll('.hs-fieldtype-text, .hs-fieldtype-phonenumber, .hs-fieldtype-booleancheckbox'); + inputWrappers.forEach((input) => { + const inputElement = input.querySelector('input'); + const inputLabel = input.querySelector('label'); + if (inputElement && inputLabel) { + input.prepend(inputLabel); + input.prepend(inputElement); + input.querySelector('.input').remove(); } }); - } - return button; -} - -function createHeading(fd) { - const heading = document.createElement('h3'); - heading.textContent = fd.Label; - return heading; -} - -function createInput(fd) { - const input = document.createElement('input'); - input.type = fd.Type; - input.id = fd.Field; - input.setAttribute('placeholder', fd.Placeholder); - if (fd.Mandatory === 'x') { - input.setAttribute('required', 'required'); - } - return input; -} - -function createTextArea(fd) { - const input = document.createElement('textarea'); - input.id = fd.Field; - input.setAttribute('placeholder', fd.Placeholder); - if (fd.Mandatory === 'x') { - input.setAttribute('required', 'required'); - } - return input; + }); } -function createLabel(fd) { - const label = document.createElement('label'); - label.setAttribute('for', fd.Field); - label.textContent = fd.Label; - if (fd.Mandatory === 'x') { - label.classList.add('required'); - } - return label; +export default function decorate(block) { + block.dataset.formId = block.children[0].children[0].innerHTML; + block.innerHTML = 'loading'; } -function applyRules(form, rules) { - const payload = constructPayload(form); - rules.forEach((field) => { - const { type, condition: { key, operator, value } } = field.rule; - if (type === 'visible') { - if (operator === 'eq') { - if (payload[key] === value) { - form.querySelector(`.${field.fieldId}`).classList.remove('hidden'); - } else { - form.querySelector(`.${field.fieldId}`).classList.add('hidden'); - } - } - } - }); -} +export async function initializeHubspot() { + await loadScript('https://js.hsforms.net/forms/embed/v2.js'); -async function createForm(formURL) { - const { pathname } = new URL(formURL); - const resp = await fetch(pathname); - const json = await resp.json(); - const form = document.createElement('form'); - const rules = []; - // eslint-disable-next-line prefer-destructuring - form.dataset.action = pathname.split('.json')[0]; - json.data.forEach((fd) => { - fd.Type = fd.Type || 'text'; - const fieldWrapper = document.createElement('div'); - const style = fd.Style ? ` form-${fd.Style}` : ''; - const fieldId = `form-${fd.Type}-wrapper${style}`; - fieldWrapper.className = fieldId; - fieldWrapper.classList.add('field-wrapper'); - switch (fd.Type) { - case 'select': - fieldWrapper.append(createLabel(fd)); - fieldWrapper.append(createSelect(fd)); - break; - case 'heading': - fieldWrapper.append(createHeading(fd)); - break; - case 'checkbox': - fieldWrapper.append(createInput(fd)); - fieldWrapper.append(createLabel(fd)); - break; - case 'text-area': - fieldWrapper.append(createLabel(fd)); - fieldWrapper.append(createTextArea(fd)); - break; - case 'submit': - fieldWrapper.append(createButton(fd)); - break; - default: - fieldWrapper.append(createInput(fd)); - fieldWrapper.append(createLabel(fd)); - } - - if (fd.Rules) { - try { - rules.push({ fieldId, rule: JSON.parse(fd.Rules) }); - } catch (e) { - // eslint-disable-next-line no-console - console.warn(`Invalid Rule ${fd.Rules}: ${e}`); - } - } - form.append(fieldWrapper); + const hubspotForms = document.querySelectorAll('.form[data-form-id]'); + hubspotForms.forEach((form, index) => { + form.dataset.formIndex = `${index}`; + window.hbspt.forms.create({ + region: 'na1', + portalId: '3458432', + formId: form.dataset.formId, + target: `[data-form-id][data-form-index='${form.dataset.formIndex}']`, + }); }); - form.addEventListener('change', () => applyRules(form, rules)); - applyRules(form, rules); - - return (form); -} - -export default async function decorate(block) { - const form = block.querySelector('a[href$=".json"]'); - block.id = 'form'; - if (form) { - form.parentElement.replaceWith(await createForm(form.href)); - } - const formRows = block.querySelectorAll('.form>div'); - formRows[0].classList.add('form-content'); - formRows[1].classList.add('form-thank-you', 'hidden'); + const debouncedRearrangeInputLabels = debounce(() => rearrangeInputLabels()); + debouncedRearrangeInputLabels(); } diff --git a/scripts/delayed.js b/scripts/delayed.js index f30b766..f098110 100644 --- a/scripts/delayed.js +++ b/scripts/delayed.js @@ -1,10 +1,14 @@ // eslint-disable-next-line import/no-cycle import { sampleRUM } from './lib-franklin.js'; import injectStarsLayers from './inject-stars-layers.js'; +import { initializeHubspot } from '../blocks/form/form.js'; // Core Web Vitals RUM collection sampleRUM('cwv'); injectStarsLayers(); +setTimeout(() => { + initializeHubspot(); +}, 2000); /* IMPORTANT: this is just for fun and not actually used. Please ignore this :) */ let lastKeys = ''; diff --git a/styles/styles.css b/styles/styles.css index 78b5d81..78f2d3b 100644 --- a/styles/styles.css +++ b/styles/styles.css @@ -21,10 +21,14 @@ :root, .theme-default { /* colors */ --color-purple: #1C0A4D; + --rgb-purple: 28 10 77; --color-light-purple: #5764EF; + --rgb-light-purple: 87 100 239; + --error-color: #ff4242; --link-color: #035fe6; --link-hover-color: #136ff6; --background-color: var(--color-purple); + --background-color-transparent: rgba(var(--rgb-purple) / 75%); --overlay-background-color: #eee; --highlight-background-color: #ccc; --text-color: #fff; @@ -72,7 +76,9 @@ .theme-light-purple { /* colors */ + --error-color: #ff8787; --background-color: var(--color-light-purple); + --background-color-transparent: rgba(var(--rgb-light-purple) / 75%); --button-color: var(--color-purple); --button-border-color: transparent; --button-color-hover: var(--link-hover-color); @@ -238,7 +244,7 @@ pre { /* buttons */ -a.button:any-link, button, main .form .button, header nav li:nth-last-child(2) a { +a.button:any-link, button, form .hs-button, main .form .button, header nav li:nth-last-child(2) a { font-family: var(--body-font-family); display: inline-flex; position: relative; @@ -265,6 +271,7 @@ nav ul li:nth-last-child(2) a { a.button:hover, a.button:focus, button:hover, button:focus, +form .hs-button:hover, form .hs-button:focus, main .form .button:hover, main .form .button:focus, nav ul li a:hover, nav ul li a:focus { background-color: var(--button-color-hover); @@ -290,7 +297,7 @@ a.button.secondary, button.secondary { } -main input, +main input:not([type='submit']), main textarea { box-sizing: border-box; display: block; @@ -299,16 +306,16 @@ main textarea { padding: 0.75rem 0.6rem; margin-bottom: 1rem; font-family: var(--body-font-family); - font-size: 1.25rem; + font-size: 18px; line-height: 24px; - border: 1px solid var(--text-color); - border-radius: 0.25rem; color: var(--text-color); - background-color: var(--background-color); + background-color: var(--background-color-transparent); } -main input:hover { - border: 1px solid var(--text-color); +main input:not([type='submit']) { + border: 1px solid transparent; + border-bottom: 1px solid var(--text-color); + border-radius: 0; } main .section {