mirror of
https://github.com/formbricks/formbricks.git
synced 2026-05-08 02:43:06 -05:00
Improve Preview in Survey Editor with Mobile & Desktop View (#573)
* made modal component responsive * added tab switch * added mobile preview mode for surveys * did some refactors * did some refactors * added type defs * ran pnpm format * removed an unused comment * fixed variable name typo * fixed UI bugs and added mobile mockup to link surveys * restored changes from fix long description PR * fixed scroll to top issue and toggle hide bug * fixed minor animation bug * fixed placement issue * re-embed restart button, make phone preview more responsive --------- Co-authored-by: Matthias Nannt <mail@matthiasnannt.com>
This commit is contained in:
committed by
GitHub
parent
52a09aa3ae
commit
98cdf941e6
@@ -1,20 +1,23 @@
|
||||
import { getPlacementStyle } from "@/lib/preview";
|
||||
import { cn } from "@formbricks/lib/cn";
|
||||
import { PlacementType } from "@formbricks/types/js";
|
||||
import { ReactNode, useEffect, useMemo, useState } from "react";
|
||||
import { ReactNode, useEffect, useMemo, useState, useRef } from "react";
|
||||
|
||||
export default function Modal({
|
||||
children,
|
||||
isOpen,
|
||||
placement,
|
||||
previewMode,
|
||||
highlightBorderColor,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
isOpen: boolean;
|
||||
placement: PlacementType;
|
||||
previewMode: string;
|
||||
highlightBorderColor: string | null | undefined;
|
||||
}) {
|
||||
const [show, setShow] = useState(false);
|
||||
const modalRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
const highlightBorderColorStyle = useMemo(() => {
|
||||
if (!highlightBorderColor) return {};
|
||||
@@ -29,15 +32,35 @@ export default function Modal({
|
||||
setShow(isOpen);
|
||||
}, [isOpen]);
|
||||
|
||||
// scroll to top whenever question in modal changes
|
||||
useEffect(() => {
|
||||
if (modalRef.current) {
|
||||
modalRef.current.scrollTop = 0;
|
||||
}
|
||||
}, [children]);
|
||||
|
||||
const slidingAnimationClass =
|
||||
previewMode === "desktop"
|
||||
? show
|
||||
? "translate-x-0 opacity-100"
|
||||
: "translate-x-32 opacity-0"
|
||||
: previewMode === "mobile"
|
||||
? show
|
||||
? "bottom-0"
|
||||
: "-bottom-full"
|
||||
: "";
|
||||
|
||||
return (
|
||||
<div aria-live="assertive" className="relative h-full w-full">
|
||||
<div aria-live="assertive" className="relative h-full w-full overflow-hidden">
|
||||
<div
|
||||
ref={modalRef}
|
||||
style={highlightBorderColorStyle}
|
||||
className={cn(
|
||||
show ? "translate-x-0 opacity-100" : "translate-x-32 opacity-0",
|
||||
"pointer-events-auto absolute h-fit max-h-[90%] w-full max-w-sm overflow-hidden overflow-y-auto rounded-lg bg-white shadow-lg ring-1 ring-black ring-opacity-5 transition-all duration-500 ease-in-out",
|
||||
getPlacementStyle(placement)
|
||||
)}
|
||||
style={highlightBorderColorStyle}>
|
||||
"pointer-events-auto absolute max-h-[90%] w-full h-fit max-w-sm overflow-y-auto rounded-lg bg-white shadow-lg ring-1 ring-black ring-opacity-5 transition-all duration-500 ease-in-out",
|
||||
previewMode === "desktop" ? getPlacementStyle(placement) : "max-w-full ",
|
||||
slidingAnimationClass
|
||||
)}>
|
||||
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -117,7 +117,7 @@ export default function MultipleChoiceMultiQuestion({
|
||||
<div className="mt-4">
|
||||
<fieldset>
|
||||
<legend className="sr-only">Options</legend>
|
||||
<div className="xs:max-h-[41vh] relative max-h-[60vh] space-y-2 overflow-y-auto rounded-md py-0.5 pr-2">
|
||||
<div className="relative space-y-2 rounded-md py-0.5">
|
||||
{questionChoices.map((choice) => (
|
||||
<div key={choice.id}>
|
||||
<label
|
||||
|
||||
@@ -102,7 +102,7 @@ export default function MultipleChoiceSingleQuestion({
|
||||
<div className="mt-4">
|
||||
<fieldset>
|
||||
<legend className="sr-only">Options</legend>
|
||||
<div className="xs:max-h-[41vh] relative max-h-[60vh] space-y-2 overflow-y-auto rounded-md py-0.5 pr-2">
|
||||
<div className="relative space-y-2 rounded-md py-0.5">
|
||||
{questionChoices.map((choice, idx) => (
|
||||
<label
|
||||
key={choice.id}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import { ReactNode } from "react";
|
||||
|
||||
export default function OptionButton({
|
||||
active,
|
||||
icon,
|
||||
onClick,
|
||||
}: {
|
||||
active: boolean;
|
||||
icon: ReactNode;
|
||||
onClick: () => void;
|
||||
}) {
|
||||
return (
|
||||
<div className={`${active ? "rounded-full bg-slate-200" : ""} cursor-pointer`} onClick={onClick}>
|
||||
{icon}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user