Skip to content

Commit 1b20dbd

Browse files
committed
feat: floating labels
1 parent 17f6f67 commit 1b20dbd

File tree

2 files changed

+63
-0
lines changed

2 files changed

+63
-0
lines changed

blocks/form/form.css

+36
Original file line numberDiff line numberDiff line change
@@ -545,4 +545,40 @@ main .form .form-image-wrapper img {
545545
main form .wizard > .current-wizard-step.panel-wrapper:first-of-type ~ .wizard-button-wrapper > .wizard-button-prev,
546546
main form .wizard > .current-wizard-step.panel-wrapper:last-of-type ~ .wizard-button-wrapper > .wizard-button-next {
547547
display: none;
548+
}
549+
550+
/** custom css goes here */
551+
552+
/* Base style for labels */
553+
main .form :not(.checkbox-wrapper, .radio-wrapper) > label {
554+
margin: 15px 5px 0;
555+
position: absolute;
556+
transition: .3s cubic-bezier(.25,.8,.5,1);
557+
}
558+
559+
main .form [data-empty='true'] > input:not(:focus) {
560+
color: transparent;
561+
}
562+
563+
/* Style for labels when input is active or not empty */
564+
main .form [data-active='true'] > label,
565+
main .form [data-empty='false'] > label {
566+
margin-left: 0;
567+
font-size: var(--form-font-size-xs);
568+
color: var(--label-color);
569+
transform: translateY(-30px); /* move as per the height of input elements */
570+
z-index: 1;
571+
}
572+
573+
/* remove placeholder when not focused */
574+
main .form input:placeholder-shown:not(:focus)::placeholder {
575+
color: transparent;
576+
}
577+
578+
/* remove default browser placeholder for date field when not focused */
579+
input[type='date']:not(:focus):in-range::-webkit-datetime-edit-year-field,
580+
input[type='date']:not(:focus):in-range::-webkit-datetime-edit-month-field,
581+
input[type='date']:not(:focus):in-range::-webkit-datetime-edit-day-field,
582+
input[type='date']:not(:focus):in-range::-webkit-datetime-edit-text {
583+
color: transparent;
548584
}

blocks/form/form.js

+27
Original file line numberDiff line numberDiff line change
@@ -502,5 +502,32 @@ export default async function decorate(block) {
502502
form.dataset.formpath = formDef.properties['fd:path'];
503503
}
504504
container.replaceWith(form);
505+
506+
/** custom code starts here */
507+
508+
const inputs = document.querySelectorAll('.field-wrapper input, .field-wrapper textarea, .field-wrapper select');
509+
// check if the input value is empty
510+
// if the input is of type select then check if the selected option is empty
511+
const isEmpty = (input) => (input.tagName !== 'SELECT' && !input.value)
512+
|| (input.tagName === 'SELECT' && input.querySelector('option[selected]') == null);
513+
514+
// exclude types that should not have the floating label effect
515+
const excludeTypes = ['checkbox', 'radio'];
516+
517+
inputs.forEach((input) => {
518+
if (!excludeTypes.includes(input.type)) {
519+
const wrapper = input.closest('.field-wrapper');
520+
input.addEventListener('focus', () => {
521+
wrapper.dataset.active = 'true';
522+
wrapper.dataset.empty = isEmpty(input);
523+
});
524+
input.addEventListener('blur', () => {
525+
wrapper.removeAttribute('data-active');
526+
wrapper.dataset.empty = isEmpty(input);
527+
});
528+
// Initialize the state based on the input's current value
529+
wrapper.dataset.empty = isEmpty(input);
530+
}
531+
});
505532
}
506533
}

0 commit comments

Comments
 (0)