isa-bubbles
Version:
Tiny, dependency-free JavaScript widgets that add interactive “bubbles” to any website — such as credits, contact forms, or feedback prompts.
255 lines (232 loc) • 7.61 kB
JavaScript
export default class ContactBubble {
constructor({
textCollapsed = "✉️",
textExpanded = "send feedback ✉️",
parent = document.body,
onSubmit = () => {
alert("click on submitted");
},
// position
position = "fixed",
right = "85px",
left = "None",
top = "None",
bottom = "20px",
// colors
colorCollapsed = "#fff",
colorExpanded = "#fff",
backgroundColorCollapsed = "rgba(63, 178, 124, 0.75)",
backgroundColorExpanded = "rgba(63, 178, 124, 0.65)",
// sizing
fontSizeCollapsed = "20px",
fontSizeExpanded = "15px",
heightCollapsed = "30px",
widthCollapsed = "30px",
heightExpanded = "50px",
widthExpanded = "50px",
lineHeightCollapsed = "1",
lineHeightExpanded = "1",
// padding collapsed
paddingLeftCollapsed = "4px",
paddingRightCollapsed = "0px",
paddingTopCollapsed = "0px",
paddingBottomCollapsed = "0px",
// padding expanded
paddingLeftExpanded = "7px",
paddingRightExpanded = "11px",
paddingTopExpanded = "8px",
paddingBottomExpanded = "8px",
//form
formPosition = "fixed",
formPositionRight = "2rem",
formPositionLeft = "None",
formPositionTop = "None",
formPositionBottom = "100px",
zIndex = "5",
}) {
this.textCollapsed = textCollapsed;
this.textExpanded = textExpanded;
this.collapsed = true;
this.showForm = false;
this.onSubmit = onSubmit;
// Inject styles
if (!document.getElementById("contact-bubble-styles")) {
const style = document.createElement("style");
style.id = "contact-bubble-styles";
style.textContent = `
.contact-container {
width: 100%;
}
.contact-bubble {
position: ${position};
right: ${right};
bottom: ${bottom};
left: ${left};
top: ${top};
color: ${colorCollapsed};
height: ${heightCollapsed};
width: ${widthCollapsed};
padding-left: ${paddingLeftCollapsed};
padding-right: ${paddingRightCollapsed};
padding-top: ${paddingTopCollapsed};
padding-bottom: ${paddingBottomCollapsed};
background-color: ${backgroundColorCollapsed};
font-size: ${fontSizeCollapsed};
line-height: ${lineHeightCollapsed};
z-index: ${zIndex};
display: flex;
place-items: center;
overflow: hidden;
cursor: pointer;
transition: all 1s ease;
border-radius: 50%;
}
.bubble-contact-text{
background-color: ${backgroundColorExpanded};
color: ${colorExpanded};
font-size: ${fontSizeExpanded};
height: ${heightExpanded};
width: ${widthExpanded};
padding-left: ${paddingLeftExpanded};
padding-right: ${paddingRightExpanded};
padding-top: ${paddingTopExpanded};
padding-bottom: ${paddingBottomExpanded};
line-height: ${lineHeightExpanded};
z-index: ${zIndex};
text-align: center;
transition: all 1s ease;
display: flex;
place-items: center;
border-radius: 50%;
}
.contact-form {
font-size: 0.9rem;
padding: 5%;
position: ${formPosition};
bottom: ${formPositionBottom};
right: ${formPositionRight};
left: ${formPositionLeft};
top: ${formPositionTop};
background: white;
border-radius: 16px;
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.2);
padding: 20px;
color: rgba(103, 111, 107, 0.85);
width: 250px;
animation: floatUp 1s ease forwards; /* changed from popIn */
z-index: 1000;
}
floatUp {
0% {
transform: translateY(50px);
opacity: 0;
}
100% {
transform: translateY(0);
opacity: 1;
}
}
.contact-form h4 {
margin-top: 0;
color: #0b440e;
text-align: center;
}
.contact-form input,
.contact-form textarea {
width: 90%;
margin-top: 0.5rem;
padding: 0.5rem;
border-radius: 8px;
border: 1px solid #ccc;
font-size: 0.9rem;
}
.contact-form button {
background-color: rgba(63, 178, 124, 0.75);
color: white;
border: none;
border-radius: 8px;
padding: 0.6rem;
margin-top: 0.8rem;
width: 100%;
cursor: pointer;
font-weight: 600;
transition: transform 0.2s ease;
}
.contact-form button:hover {
transform: scale(1.02);
background-color: rgba(63, 178, 124, 0.95);
}
.close-btn {
position: absolute;
top: 10px;
right: 25px;
font-size: 1.2rem;
font-weight: 600;
color: rgba(63, 178, 124, 0.75);
background: none;
border: none;
cursor: pointer;
transition: transform 0.2s ease, color 0.2s ease;
}
.close-btn:hover {
transform: scale(1.2);
color: rgba(63, 178, 124, 0.95); /* darker hover green */
}`;
document.head.appendChild(style);
}
// Container
this.container = document.createElement("div");
this.container.className = "contact-container";
// Bubble
this.bubble = document.createElement("div");
this.bubble.className = "contact-bubble";
this.bubble.textContent = this.textCollapsed;
this.bubble.addEventListener("mouseenter", () => {
this.bubble.classList.add("bubble-contact-text");
this.bubble.textContent = this.textExpanded;
});
this.bubble.addEventListener("mouseleave", () => {
this.bubble.classList.remove("bubble-contact-text");
this.bubble.textContent = this.textCollapsed;
});
this.bubble.addEventListener("click", () => {
this.toggleForm();
});
this.container.appendChild(this.bubble);
parent.appendChild(this.container);
}
toggleForm() {
if (this.showForm) {
this.form.remove();
this.showForm = false;
} else {
this.showForm = true;
this.createForm();
}
}
createForm() {
this.form = document.createElement("form");
this.form.className = "contact-form";
this.form.innerHTML = `
<div class="close-btn">x</div>
<div class="contact-form-text">
Have feedback or just want to connect?<br/>
Send me a quick message — I will get back to you soon!
</div>
<input type="text" name="user_name" placeholder="Your name" required />
<input type="email" name="user_email" placeholder="Your email" required />
<textarea name="message" placeholder="Your message" required></textarea>
<button type="submit">Send</button>
`;
this.form.querySelector(".close-btn").onclick = () => this.toggleForm();
this.form.onsubmit = (e) => {
e.preventDefault();
this.toggleForm();
this.onSubmit();
};
this.container.appendChild(this.form);
}
}
// if (typeof window !== "undefined") {
// window.ContactBubble = ContactBubble;
// }