mirror of
https://github.com/formbricks/formbricks.git
synced 2025-12-30 02:10:12 -06:00
fix: Mobile height inconsistencies (#2540)
Co-authored-by: Dhruwang <dhruwangjariwala18@gmail.com> Co-authored-by: Matthias Nannt <mail@matthiasnannt.com>
This commit is contained in:
@@ -28,7 +28,7 @@ const DemoPreview: React.FC<DemoPreviewProps> = ({ template }) => {
|
||||
<div className="flex items-center justify-center rounded-xl border-2 border-slate-300 bg-slate-200 py-6 transition-transform duration-150 dark:border-slate-500 dark:bg-slate-700">
|
||||
<div className="flex flex-col items-center justify-around">
|
||||
<p className="my-3 text-sm text-slate-500 dark:text-slate-300">Preview</p>
|
||||
<div className="">
|
||||
<div>
|
||||
{selectedTemplate && (
|
||||
<PreviewSurvey
|
||||
activeQuestionId={activeQuestionId}
|
||||
|
||||
@@ -36,7 +36,7 @@ export const GitHubSponsorship: React.FC = () => {
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center justify-end">
|
||||
{/* <Image src={PHIcon} alt="Product Hunt Logo" width={80} className="" /> */}
|
||||
{/* <Image src={PHIcon} alt="Product Hunt Logo" width={80} /> */}
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
@@ -7,7 +7,7 @@ export default function HeadingCentered() {
|
||||
const router = useRouter();
|
||||
return (
|
||||
<div className="mx-auto grid grid-cols-1 content-center gap-10 pb-12 pt-24 md:grid-cols-2">
|
||||
<div className="">
|
||||
<div>
|
||||
<p className="text-md text-brand-dark dark:text-brand-light font-semibold uppercase">
|
||||
What are you waiting for?
|
||||
</p>
|
||||
|
||||
@@ -7,7 +7,7 @@ export default function HeadingCentered() {
|
||||
const router = useRouter();
|
||||
return (
|
||||
<div className="mx-auto grid max-w-md grid-cols-1 content-center gap-10 px-4 py-12 sm:max-w-3xl sm:px-6 md:grid-cols-2 md:pb-36 md:pt-24 lg:max-w-6xl lg:px-8">
|
||||
<div className="">
|
||||
<div>
|
||||
<p className="text-md text-brand-dark dark:text-brand-light mb-3 font-semibold uppercase">
|
||||
What are you waiting for?
|
||||
</p>
|
||||
|
||||
@@ -13,7 +13,7 @@ export default function Layout({ title, description, children }: LayoutProps) {
|
||||
<div className="mx-auto bg-gradient-to-br from-slate-800 via-slate-900 to-slate-900">
|
||||
<MetaInformation title={title} description={description} />
|
||||
<HeaderTribe />
|
||||
<main className="">{children}</main>
|
||||
<main>{children}</main>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -562,15 +562,15 @@ export default function FormTribeHackathon() {
|
||||
<div className="max-w-8xl mt-8 grid grid-cols-3 gap-4 px-56">
|
||||
<div className="px-8">
|
||||
<p className="h-0 text-lg font-bold text-slate-300">+ Sticker Set</p>
|
||||
<Image src={ArrowSticker} alt="rookie batch" className="" />
|
||||
<Image src={ArrowSticker} alt="rookie batch" />
|
||||
</div>
|
||||
<div className="px-8">
|
||||
<p className="h-0 text-lg font-bold text-slate-300">+ Hoodie</p>
|
||||
<Image src={HoodieSticker} alt="rookie batch" className="" />
|
||||
<Image src={HoodieSticker} alt="rookie batch" />
|
||||
</div>
|
||||
<div className="px-8">
|
||||
<p className="h-0 text-lg font-bold text-slate-300">+ Handmade Gift</p>
|
||||
<Image src={ArrowGift} alt="rookie batch" className="" />
|
||||
<Image src={ArrowGift} alt="rookie batch" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -754,7 +754,7 @@ export default function FormTribeHackathon() {
|
||||
</div>
|
||||
|
||||
{FAQ.map((question) => (
|
||||
<div key={question.question} className="">
|
||||
<div key={question.question}>
|
||||
<div>
|
||||
<h3 className="mt-6 text-lg font-bold text-slate-700">{question.question} </h3>
|
||||
<p className="text-slate-600">{question.answer}</p>
|
||||
|
||||
@@ -415,7 +415,7 @@ export default function LinkSurveyPage() {
|
||||
headline="Try THE open source form builder 💪"
|
||||
subheadline="Convinced that Formbricks is a good open source Typeform alternative? Try it now!"
|
||||
/>
|
||||
<div className="">
|
||||
<div>
|
||||
<HeadingCentered
|
||||
heading="All form builder features"
|
||||
teaser="Build open source forms like never before"
|
||||
@@ -440,7 +440,7 @@ export default function LinkSurveyPage() {
|
||||
imgAlt="Peer Richelsen, Co-Founder and CEO of Cal.com"
|
||||
textSize="large"
|
||||
/>
|
||||
<div className="">
|
||||
<div>
|
||||
<HeadingCentered heading="All question types included" teaser="A complete open source form builder" />
|
||||
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
|
||||
{allQuestionTypes.map((feature) => {
|
||||
|
||||
@@ -266,7 +266,7 @@ export default function WebsiteSurveyPage() {
|
||||
headline="All clear? Run your first website survey 👉"
|
||||
subheadline="Targeted website surveys, all privacy-first. Run professional research without compromising data privacy."
|
||||
/>
|
||||
<div className="">
|
||||
<div>
|
||||
<HeadingCentered
|
||||
heading={
|
||||
<span>
|
||||
|
||||
@@ -41,7 +41,7 @@ export default function AttributeSettingsTab({ attributeClass, setOpen }: Attrib
|
||||
return (
|
||||
<div>
|
||||
<form className="space-y-4" onSubmit={handleSubmit(onSubmit)}>
|
||||
<div className="">
|
||||
<div>
|
||||
<Label className="text-slate-600">Name</Label>
|
||||
<Input
|
||||
type="text"
|
||||
@@ -51,7 +51,7 @@ export default function AttributeSettingsTab({ attributeClass, setOpen }: Attrib
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
<div className="">
|
||||
<div>
|
||||
<Label className="text-slate-600">Description</Label>
|
||||
<Input
|
||||
type="text"
|
||||
|
||||
@@ -393,7 +393,7 @@ export default function Navigation({
|
||||
<DropdownMenuSubTrigger>
|
||||
<div>
|
||||
<div className="flex items-center space-x-1">
|
||||
<p className="">{truncate(product!.name, 20)}</p>
|
||||
<p>{truncate(product!.name, 20)}</p>
|
||||
{!widgetSetupCompleted && (
|
||||
<TooltipProvider delayDuration={50}>
|
||||
<Tooltip>
|
||||
|
||||
@@ -96,7 +96,7 @@ export default function EditAPIKeys({
|
||||
<div className="col-span-4 sm:col-span-2">Label</div>
|
||||
<div className="col-span-4 hidden sm:col-span-5 sm:block">API Key</div>
|
||||
<div className="col-span-4 sm:col-span-2">Created at</div>
|
||||
<div className=""></div>
|
||||
<div></div>
|
||||
</div>
|
||||
<div className="grid-cols-9">
|
||||
{apiKeysLocal && apiKeysLocal.length === 0 ? (
|
||||
|
||||
@@ -52,7 +52,7 @@ export default function EditAlerts({
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<div className="col-span-1 flex cursor-default items-center justify-center space-x-2">
|
||||
<span className="">Every Response</span>
|
||||
<span>Every Response</span>
|
||||
<HelpCircleIcon className="h-4 w-4 flex-shrink-0 text-slate-500" />
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
|
||||
@@ -124,7 +124,7 @@ export const ShareEmbedSurvey = ({ survey, open, setOpen, webAppUrl, user }: Sha
|
||||
))}
|
||||
</div>
|
||||
<div className="col-span-4 h-full overflow-y-auto bg-slate-50 px-4 py-6 lg:col-span-3 lg:p-6">
|
||||
<div className="">
|
||||
<div>
|
||||
{activeId === "email" ? (
|
||||
<EmailTab surveyId={survey.id} email={email} />
|
||||
) : activeId === "webpage" ? (
|
||||
|
||||
@@ -107,7 +107,7 @@ export default function EmailTab({ surveyId, email }: EmailTabProps) {
|
||||
<div className="h-3 w-3 rounded-full bg-amber-500"></div>
|
||||
<div className="h-3 w-3 rounded-full bg-emerald-500"></div>
|
||||
</div>
|
||||
<div className="">
|
||||
<div>
|
||||
<div className="mb-2 border-b border-slate-200 pb-2 text-sm">To : {email || "user@mail.com"}</div>
|
||||
<div className="border-b border-slate-200 pb-2 text-sm">
|
||||
Subject : Formbricks Email Survey Preview
|
||||
|
||||
@@ -61,7 +61,7 @@ export default function LinkTab({ survey, webAppUrl, surveyUrl, setSurveyUrl }:
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="">
|
||||
<div>
|
||||
<p className="mb-2 pt-2 font-semibold text-slate-700">Survey link got too long? Shorten it!</p>
|
||||
<div className="rounded-md border border-slate-200 bg-white">
|
||||
<UrlShortenerForm webAppUrl={webAppUrl} />
|
||||
|
||||
@@ -17,7 +17,7 @@ export default function WebpageTab({ surveyUrl }) {
|
||||
return (
|
||||
<div className="flex h-full grow flex-col">
|
||||
<div className="flex justify-between">
|
||||
<div className=""></div>
|
||||
<div></div>
|
||||
<Button
|
||||
variant="darkCTA"
|
||||
title="Embed survey in your website"
|
||||
|
||||
@@ -156,14 +156,15 @@ export const MatrixQuestionForm = ({
|
||||
</div>
|
||||
))}
|
||||
<Button
|
||||
variant="minimal"
|
||||
className="mt-2 space-x-2"
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
className="mt-3"
|
||||
StartIcon={PlusIcon}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
handleAddLabel("row");
|
||||
}}>
|
||||
<PlusIcon className="h-4 w-4" />
|
||||
<span>Add Row</span>
|
||||
<span>Add row</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -195,14 +196,15 @@ export const MatrixQuestionForm = ({
|
||||
</div>
|
||||
))}
|
||||
<Button
|
||||
variant="minimal"
|
||||
className="mt-2 space-x-2"
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
className="mt-3"
|
||||
StartIcon={PlusIcon}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
handleAddLabel("column");
|
||||
}}>
|
||||
<PlusIcon className="h-4 w-4" />
|
||||
<span>Add Column</span>
|
||||
<span>Add column</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -127,7 +127,7 @@ export default function RecontactOptionsCard({
|
||||
id={option.name}
|
||||
className="aria-checked:border-brand-dark mx-5 disabled:border-slate-400 aria-checked:border-2"
|
||||
/>
|
||||
<div className="">
|
||||
<div>
|
||||
<p className="font-semibold text-slate-700">{option.name}</p>
|
||||
|
||||
<p className="mt-2 text-xs font-normal text-slate-600">{option.description}</p>
|
||||
@@ -188,7 +188,7 @@ export default function RecontactOptionsCard({
|
||||
id="newDays"
|
||||
className="aria-checked:border-brand-dark mx-4 disabled:border-slate-400 aria-checked:border-2"
|
||||
/>
|
||||
<div className="">
|
||||
<div>
|
||||
<p className="text-sm font-semibold text-slate-700">
|
||||
Wait
|
||||
<Input
|
||||
|
||||
@@ -322,7 +322,7 @@ export default function WhenToSendCard({
|
||||
<label
|
||||
htmlFor="triggerDelay"
|
||||
className="flex w-full cursor-pointer items-center rounded-lg border bg-slate-50 p-4">
|
||||
<div className="">
|
||||
<div>
|
||||
<p className="text-sm font-semibold text-slate-700">
|
||||
Wait
|
||||
<Input
|
||||
|
||||
@@ -52,7 +52,7 @@ export default function SetupInstructionsOnboarding({
|
||||
))}
|
||||
</nav>
|
||||
</div>
|
||||
<div className="">
|
||||
<div>
|
||||
{activeTab === "npm" ? (
|
||||
<div className="prose prose-slate">
|
||||
<CodeBlock customEditorClass="!bg-white border border-slate-200" language="sh">
|
||||
|
||||
@@ -15,7 +15,7 @@ export default function RootLayout({ children }: { children: React.ReactNode })
|
||||
return (
|
||||
<html lang="en">
|
||||
{process.env.VERCEL === "1" && <SpeedInsights sampleRate={0.1} />}
|
||||
<body className="flex h-screen flex-col">{children}</body>
|
||||
<body className="flex h-dvh flex-col">{children}</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -118,9 +118,9 @@ export default function LinkSurvey({
|
||||
setAutofocus(true);
|
||||
}
|
||||
// For safari on mobile devices, scroll is a bit off due to dynamic height of address bar, so on inital load, we scroll to the bottom
|
||||
window.scrollTo({
|
||||
top: document.body.scrollHeight,
|
||||
});
|
||||
// window.scrollTo({
|
||||
// top: document.body.scrollHeight,
|
||||
// });
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
@@ -185,7 +185,7 @@ export default function LinkSurvey({
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex h-screen items-end justify-center md:items-center">
|
||||
<div className="flex max-h-dvh min-h-dvh items-end justify-center overflow-clip md:items-center">
|
||||
{!determineStyling().isLogoHidden && product.logo?.url && <ClientLogo product={product} />}
|
||||
<ContentWrapper className="w-full p-0 md:max-w-md">
|
||||
{isPreview && (
|
||||
|
||||
@@ -116,28 +116,25 @@ export const MediaBackground: React.FC<MediaBackgroundProps> = ({
|
||||
<div
|
||||
className={`${baseClasses} ${loadedClass} bg-cover bg-center`}
|
||||
style={{ backgroundImage: `url(${background?.bg})`, filter: `${filterStyle}` }}></div>
|
||||
<div className={`absolute bottom-6 z-10 h-12 w-full lg:bottom-0`}>
|
||||
<div className="mx-auto max-w-full p-3 text-center text-xs text-slate-400 lg:text-right">
|
||||
{authorDetailsForUnsplash.authorName && (
|
||||
<div className="ml-auto w-max">
|
||||
<span>Photo by </span>
|
||||
<Link
|
||||
href={authorDetailsForUnsplash.authorURL + "?utm_source=formbricks&utm_medium=referral"}
|
||||
target="_blank"
|
||||
className="hover:underline">
|
||||
{authorDetailsForUnsplash.authorName}
|
||||
</Link>
|
||||
<span> on </span>
|
||||
<Link
|
||||
href="https://unsplash.com/?utm_source=formbricks&utm_medium=referral"
|
||||
target="_blank"
|
||||
className="hover:underline">
|
||||
Unsplash
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{authorDetailsForUnsplash.authorName && (
|
||||
<div className="absolute bottom-4 right-6 z-10 ml-auto hidden w-max text-xs text-slate-400 md:block">
|
||||
<span>Photo by </span>
|
||||
<Link
|
||||
href={authorDetailsForUnsplash.authorURL + "?utm_source=formbricks&utm_medium=referral"}
|
||||
target="_blank"
|
||||
className="hover:underline">
|
||||
{authorDetailsForUnsplash.authorName}
|
||||
</Link>
|
||||
<span> on </span>
|
||||
<Link
|
||||
href="https://unsplash.com/?utm_source=formbricks&utm_medium=referral"
|
||||
target="_blank"
|
||||
className="hover:underline">
|
||||
Unsplash
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
case "upload":
|
||||
@@ -178,7 +175,7 @@ export const MediaBackground: React.FC<MediaBackgroundProps> = ({
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col items-center justify-center">
|
||||
<div className="flex min-h-dvh flex-col items-center justify-center">
|
||||
{renderBackground()}
|
||||
<div className="relative w-full">{children}</div>
|
||||
</div>
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
export default async function SurveyLayout({ children }) {
|
||||
return <div className="h-full">{children}</div>;
|
||||
return <div className="h-dvh">{children}</div>;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,29 @@
|
||||
const monthNames = [
|
||||
"January",
|
||||
"February",
|
||||
"March",
|
||||
"April",
|
||||
"May",
|
||||
"June",
|
||||
"July",
|
||||
"August",
|
||||
"September",
|
||||
"October",
|
||||
"November",
|
||||
"December",
|
||||
];
|
||||
|
||||
// Helper function to calculate difference in days between two dates
|
||||
export const diffInDays = (date1: Date, date2: Date) => {
|
||||
const diffTime = Math.abs(date2.getTime() - date1.getTime());
|
||||
return Math.floor(diffTime / (1000 * 60 * 60 * 24));
|
||||
};
|
||||
|
||||
// Helper function to get the month name
|
||||
export const getMonthName = (monthIndex: number) => {
|
||||
return monthNames[monthIndex];
|
||||
};
|
||||
|
||||
export const formatDateWithOrdinal = (date: Date): string => {
|
||||
const getOrdinalSuffix = (day: number) => {
|
||||
const suffixes = ["th", "st", "nd", "rd"];
|
||||
@@ -12,20 +32,6 @@ export const formatDateWithOrdinal = (date: Date): string => {
|
||||
};
|
||||
|
||||
const dayOfWeekNames = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
|
||||
const monthNames = [
|
||||
"January",
|
||||
"February",
|
||||
"March",
|
||||
"April",
|
||||
"May",
|
||||
"June",
|
||||
"July",
|
||||
"August",
|
||||
"September",
|
||||
"October",
|
||||
"November",
|
||||
"December",
|
||||
];
|
||||
|
||||
const dayOfWeek = dayOfWeekNames[date.getDay()];
|
||||
const day = date.getDate();
|
||||
@@ -35,6 +41,22 @@ export const formatDateWithOrdinal = (date: Date): string => {
|
||||
return `${dayOfWeek}, ${monthNames[monthIndex]} ${day}${getOrdinalSuffix(day)}, ${year}`;
|
||||
};
|
||||
|
||||
// Helper function to format the date with an ordinal suffix
|
||||
export const getOrdinalDate = (date: number) => {
|
||||
const j = date % 10,
|
||||
k = date % 100;
|
||||
if (j === 1 && k !== 11) {
|
||||
return date + "st";
|
||||
}
|
||||
if (j === 2 && k !== 12) {
|
||||
return date + "nd";
|
||||
}
|
||||
if (j === 3 && k !== 13) {
|
||||
return date + "rd";
|
||||
}
|
||||
return date + "th";
|
||||
};
|
||||
|
||||
export function isValidDateString(value: string) {
|
||||
const regex = /^(?:\d{4}-\d{2}-\d{2}|\d{2}-\d{2}-\d{4})$/;
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ export default function CalEmbed({ question, onSuccessfulBooking }: CalEmbedProp
|
||||
}, [cal, question.calUserName]);
|
||||
|
||||
return (
|
||||
<div className="relative mt-4 max-h-[33vh] overflow-auto">
|
||||
<div className="relative mt-4 overflow-auto">
|
||||
<div id="fb-cal-embed" className={cn("border-border rounded-lg border")} />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -209,7 +209,7 @@ export default function FileInput({
|
||||
return (
|
||||
<div
|
||||
className={`items-left bg-input-bg hover:bg-input-bg-selected border-border relative mt-3 flex w-full flex-col justify-center rounded-lg border-2 border-dashed dark:border-slate-600 dark:bg-slate-700 dark:hover:border-slate-500 dark:hover:bg-slate-800`}>
|
||||
<div className="max-h-[30vh] overflow-auto">
|
||||
<div>
|
||||
{fileUrls &&
|
||||
fileUrls?.map((file, index) => {
|
||||
const fileName = getOriginalFileNameFromUrl(file);
|
||||
|
||||
@@ -63,42 +63,44 @@ export const ConsentQuestion = ({
|
||||
htmlString={getLocalizedValue(question.html, languageCode) || ""}
|
||||
questionId={question.id}
|
||||
/>
|
||||
|
||||
<label
|
||||
tabIndex={1}
|
||||
id={`${question.id}-label`}
|
||||
onKeyDown={(e) => {
|
||||
// Accessibility: if spacebar was pressed pass this down to the input
|
||||
if (e.key === " ") {
|
||||
e.preventDefault();
|
||||
document.getElementById(question.id)?.click();
|
||||
document.getElementById(`${question.id}-label`)?.focus();
|
||||
}
|
||||
}}
|
||||
className="border-border bg-input-bg text-heading hover:bg-input-bg-selected focus:bg-input-bg-selected focus:ring-brand rounded-custom relative z-10 mt-4 flex w-full cursor-pointer items-center border p-4 text-sm focus:outline-none focus:ring-2 focus:ring-offset-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
id={question.id}
|
||||
name={question.id}
|
||||
value={getLocalizedValue(question.label, languageCode)}
|
||||
onChange={(e) => {
|
||||
if (e.target instanceof HTMLInputElement && e.target.checked) {
|
||||
onChange({ [question.id]: "accepted" });
|
||||
} else {
|
||||
onChange({ [question.id]: "dismissed" });
|
||||
<div className="bg-survey-bg sticky -bottom-2 z-10 w-full px-1 py-1">
|
||||
<label
|
||||
tabIndex={1}
|
||||
id={`${question.id}-label`}
|
||||
onKeyDown={(e) => {
|
||||
// Accessibility: if spacebar was pressed pass this down to the input
|
||||
if (e.key === " ") {
|
||||
e.preventDefault();
|
||||
document.getElementById(question.id)?.click();
|
||||
document.getElementById(`${question.id}-label`)?.focus();
|
||||
}
|
||||
}}
|
||||
checked={value === "accepted"}
|
||||
className="border-brand text-brand h-4 w-4 border focus:ring-0 focus:ring-offset-0"
|
||||
aria-labelledby={`${question.id}-label`}
|
||||
required={question.required}
|
||||
/>
|
||||
<span id={`${question.id}-label`} className="ml-3 font-medium">
|
||||
{getLocalizedValue(question.label, languageCode)}
|
||||
</span>
|
||||
</label>
|
||||
className="border-border bg-input-bg text-heading hover:bg-input-bg-selected focus:bg-input-bg-selected focus:ring-brand rounded-custom relative z-10 my-2 flex w-full cursor-pointer items-center border p-4 text-sm focus:outline-none focus:ring-2 focus:ring-offset-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
id={question.id}
|
||||
name={question.id}
|
||||
value={getLocalizedValue(question.label, languageCode)}
|
||||
onChange={(e) => {
|
||||
if (e.target instanceof HTMLInputElement && e.target.checked) {
|
||||
onChange({ [question.id]: "accepted" });
|
||||
} else {
|
||||
onChange({ [question.id]: "dismissed" });
|
||||
}
|
||||
}}
|
||||
checked={value === "accepted"}
|
||||
className="border-brand text-brand h-4 w-4 border focus:ring-0 focus:ring-offset-0"
|
||||
aria-labelledby={`${question.id}-label`}
|
||||
required={question.required}
|
||||
/>
|
||||
<span id={`${question.id}-label`} className="ml-3 font-medium">
|
||||
{getLocalizedValue(question.label, languageCode)}
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</ScrollableContainer>
|
||||
|
||||
<div className="flex w-full justify-between px-6 py-4">
|
||||
{!isFirstQuestion && (
|
||||
<BackButton
|
||||
|
||||
@@ -112,10 +112,10 @@ export const DateQuestion = ({
|
||||
<span>{errorMessage}</span>
|
||||
</div>
|
||||
<div
|
||||
className={cn("mt-4", errorMessage && "rounded-lg border-2 border-red-500")}
|
||||
className={cn("mt-4 w-full", errorMessage && "rounded-lg border-2 border-red-500")}
|
||||
id="date-picker-root">
|
||||
{loading && (
|
||||
<div className="bg-survey-bg border-border text-placeholder relative flex h-12 w-full cursor-pointer appearance-none items-center justify-center rounded-lg border text-left text-base font-normal focus:outline-none focus:ring-2 focus:ring-neutral-900 focus:ring-offset-1">
|
||||
<div className="bg-survey-bg border-border text-placeholder relative flex h-16 w-full cursor-pointer appearance-none items-center justify-center rounded-lg border text-left text-base font-normal focus:outline-none focus:ring-2 focus:ring-neutral-900 focus:ring-offset-1">
|
||||
<span
|
||||
className="h-6 w-6 animate-spin rounded-full border-b-2 border-neutral-900"
|
||||
style={{ borderTopColor: "transparent" }}></span>
|
||||
|
||||
@@ -104,7 +104,7 @@ export const MatrixQuestion = ({
|
||||
subheader={getLocalizedValue(question.subheader, languageCode)}
|
||||
questionId={question.id}
|
||||
/>
|
||||
<div className="overflow-x-auto">
|
||||
<div className="overflow-x-auto py-4">
|
||||
<table className="no-scrollbar min-w-full table-auto border-collapse text-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -154,7 +154,7 @@ export const MatrixQuestion = ({
|
||||
</div>
|
||||
</div>
|
||||
</ScrollableContainer>
|
||||
<div className="flex w-full justify-between px-6">
|
||||
<div className="flex w-full justify-between px-6 py-4">
|
||||
{!isFirstQuestion && (
|
||||
<BackButton
|
||||
backButtonLabel={getLocalizedValue(question.backButtonLabel, languageCode)}
|
||||
|
||||
@@ -109,7 +109,7 @@ export const PictureSelectionQuestion = ({
|
||||
<div className="mt-4">
|
||||
<fieldset>
|
||||
<legend className="sr-only">Options</legend>
|
||||
<div className="rounded-m bg-survey-bg relative grid max-h-[33vh] grid-cols-2 gap-x-5 gap-y-4 overflow-y-auto">
|
||||
<div className="bg-survey-bg relative grid grid-cols-2 gap-x-5 gap-y-4">
|
||||
{questionChoices.map((choice, idx) => (
|
||||
<label
|
||||
key={choice.id}
|
||||
@@ -128,7 +128,7 @@ export const PictureSelectionQuestion = ({
|
||||
Array.isArray(value) && value.includes(choice.id)
|
||||
? `border-brand text-brand z-10 border-4 shadow-xl`
|
||||
: "",
|
||||
"focus:border-brand group/image relative inline-block h-28 w-full cursor-pointer overflow-hidden rounded-xl border focus:border-4 focus:outline-none"
|
||||
"focus:border-brand group/image rounded-custom relative inline-block h-28 w-full cursor-pointer overflow-hidden border focus:border-4 focus:outline-none"
|
||||
)}>
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img
|
||||
@@ -170,7 +170,7 @@ export const PictureSelectionQuestion = ({
|
||||
tabIndex={-1}
|
||||
checked={value.includes(choice.id)}
|
||||
className={cn(
|
||||
"border-border pointer-events-none absolute right-2 top-2 z-20 h-5 w-5 rounded border",
|
||||
"border-border rounded-custom pointer-events-none absolute right-2 top-2 z-20 h-5 w-5 border",
|
||||
value.includes(choice.id) ? "border-brand text-brand" : ""
|
||||
)}
|
||||
required={question.required && value.length ? false : question.required}
|
||||
|
||||
@@ -24,7 +24,7 @@ export const ScrollableContainer = ({ children }: ScrollableContainerProps) => {
|
||||
const toggleOverflow = (hide: boolean) => {
|
||||
if (timeoutRef.current) clearTimeout(timeoutRef.current);
|
||||
if (hide) {
|
||||
timeoutRef.current = setTimeout(() => setIsOverflowHidden(true), 1500);
|
||||
timeoutRef.current = setTimeout(() => setIsOverflowHidden(true), 1000);
|
||||
} else {
|
||||
setIsOverflowHidden(false);
|
||||
checkScroll();
|
||||
@@ -55,10 +55,10 @@ export const ScrollableContainer = ({ children }: ScrollableContainerProps) => {
|
||||
<div
|
||||
ref={containerRef}
|
||||
style={{
|
||||
scrollbarGutter: "stable",
|
||||
maxHeight: isSurveyPreview ? "41vh" : "60vh",
|
||||
scrollbarGutter: "stable both-edges",
|
||||
maxHeight: isSurveyPreview ? "40dvh" : "60dvh",
|
||||
}}
|
||||
className={`overflow-${isOverflowHidden ? "hidden" : "auto"} pb-1 pl-6 pr-4`}
|
||||
className={`overflow-${isOverflowHidden ? "hidden" : "auto"} px-4 pb-1`}
|
||||
onMouseEnter={() => toggleOverflow(false)}
|
||||
onTouchStart={() => toggleOverflow(false)}
|
||||
onTouchEnd={() => toggleOverflow(true)}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { useEffect, useMemo, useState } from "preact/hooks";
|
||||
import DatePicker from "react-date-picker";
|
||||
|
||||
import { getMonthName, getOrdinalDate } from "@formbricks/lib/utils/datetime";
|
||||
|
||||
const CalendarIcon = () => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
@@ -72,23 +74,19 @@ export default function Question({ defaultDate, format }: { defaultDate?: Date;
|
||||
const formattedDate = useMemo(() => {
|
||||
if (!selectedDate) return "";
|
||||
|
||||
if (format === "M-d-y") {
|
||||
return `${selectedDate?.getMonth() + 1}-${selectedDate?.getDate()}-${selectedDate?.getFullYear()}`;
|
||||
}
|
||||
const day = selectedDate.getDate();
|
||||
const monthIndex = selectedDate.getMonth();
|
||||
const year = selectedDate.getFullYear();
|
||||
|
||||
if (format === "d-M-y") {
|
||||
return `${selectedDate?.getDate()}-${selectedDate?.getMonth() + 1}-${selectedDate?.getFullYear()}`;
|
||||
}
|
||||
|
||||
return `${selectedDate?.getFullYear()}-${selectedDate?.getMonth() + 1}-${selectedDate?.getDate()}`;
|
||||
}, [format, selectedDate]);
|
||||
return `${getOrdinalDate(day)} of ${getMonthName(monthIndex)}, ${year}`;
|
||||
}, [selectedDate]);
|
||||
|
||||
return (
|
||||
<div className="relative h-40">
|
||||
<div className="relative">
|
||||
{!datePickerOpen && (
|
||||
<div
|
||||
onClick={() => setDatePickerOpen(true)}
|
||||
className="bg-input-bg hover:bg-input-bg-selected border-border text-placeholder relative flex h-40 w-full cursor-pointer appearance-none items-center justify-center rounded-lg border text-left text-base font-normal focus:outline-none focus:ring-2 focus:ring-neutral-900 focus:ring-offset-1">
|
||||
className="bg-input-bg hover:bg-input-bg-selected border-border text-heading rounded-custom relative flex h-[12dvh] w-full cursor-pointer appearance-none items-center justify-center border text-left text-base font-normal focus:outline-none focus:ring-2 focus:ring-neutral-900 focus:ring-offset-1">
|
||||
<div className="flex items-center gap-2">
|
||||
{selectedDate ? (
|
||||
<div className="flex items-center gap-2">
|
||||
@@ -119,10 +117,10 @@ export default function Question({ defaultDate, format }: { defaultDate?: Date;
|
||||
monthPlaceholder="MM"
|
||||
yearPlaceholder="YYYY"
|
||||
format={format ?? "M-d-y"}
|
||||
className={`dp-input-root rounded-custom ${!datePickerOpen ? "wrapper-hide" : ""}
|
||||
className={`dp-input-root rounded-custom wrapper-hide ${!datePickerOpen ? "" : "h-[46dvh] sm:h-[34dvh]"}
|
||||
${hideInvalid ? "hide-invalid" : ""}
|
||||
`}
|
||||
calendarClassName="calendar-root w-80 rounded-lg border border-[#e5e7eb] p-3 shadow-md h-40 overflow-auto"
|
||||
calendarClassName="calendar-root !bg-input-bg border border-border rounded-custom p-3 h-[46dvh] sm:h-[33dvh] overflow-auto"
|
||||
clearIcon={null}
|
||||
onCalendarOpen={() => {
|
||||
setDatePickerOpen(true);
|
||||
@@ -136,14 +134,14 @@ export default function Question({ defaultDate, format }: { defaultDate?: Date;
|
||||
calendarIcon={<CalendarIcon />}
|
||||
tileClassName={({ date }) => {
|
||||
const baseClass =
|
||||
"hover:bg-slate-200 rounded-md h-9 p-0 mt-1 font-normal text-slate-900 aria-selected:opacity-100";
|
||||
"hover:bg-input-bg-selected rounded-custom h-9 p-0 mt-1 font-normal text-heading aria-selected:opacity-100";
|
||||
// today's date class
|
||||
if (
|
||||
date.getDate() === new Date().getDate() &&
|
||||
date.getMonth() === new Date().getMonth() &&
|
||||
date.getFullYear() === new Date().getFullYear()
|
||||
) {
|
||||
return `${baseClass} bg-slate-100`;
|
||||
return `${baseClass} border border-input-border`;
|
||||
}
|
||||
// active date class
|
||||
if (
|
||||
@@ -151,7 +149,7 @@ export default function Question({ defaultDate, format }: { defaultDate?: Date;
|
||||
date.getMonth() === selectedDate?.getMonth() &&
|
||||
date.getFullYear() === selectedDate?.getFullYear()
|
||||
) {
|
||||
return `${baseClass} !bg-slate-900 !text-slate-100`;
|
||||
return `${baseClass} !bg-accent-selected-bg !border-border-highlight !text-heading`;
|
||||
}
|
||||
|
||||
return baseClass;
|
||||
@@ -159,6 +157,7 @@ export default function Question({ defaultDate, format }: { defaultDate?: Date;
|
||||
formatShortWeekday={(_, date) => {
|
||||
return date.toLocaleDateString("en-US", { weekday: "short" }).slice(0, 2);
|
||||
}}
|
||||
navi
|
||||
showNeighboringMonth={false}
|
||||
showLeadingZeros={false}
|
||||
/>
|
||||
|
||||
@@ -50,12 +50,14 @@
|
||||
.react-date-picker__calendar--open {
|
||||
position: absolute !important;
|
||||
top: 0 !important;
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.calendar-root {
|
||||
position: absolute !important;
|
||||
top: 0 !important;
|
||||
background: var(--fb-survey-background-color) !important;
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.calendar-root [class$="navigation"] {
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
|
||||
/* Chrome, Edge, and Safari */
|
||||
#fbjs *::-webkit-scrollbar {
|
||||
width: 8px ;
|
||||
background: transparent ;
|
||||
width: 6px;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
#fbjs *::-webkit-scrollbar-track {
|
||||
@@ -25,6 +25,12 @@
|
||||
border-radius: 10px
|
||||
}
|
||||
|
||||
/* Firefox */
|
||||
#fbjs * {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: var(--fb-brand-color) transparent;
|
||||
}
|
||||
|
||||
|
||||
/* this is for styling the HtmlBody component */
|
||||
.fb-htmlbody {
|
||||
|
||||
@@ -16,7 +16,7 @@ interface ClientLogoProps {
|
||||
export const ClientLogo = ({ environmentId, product, previewSurvey = false }: ClientLogoProps) => {
|
||||
return (
|
||||
<div
|
||||
className={cn(previewSurvey ? "" : "left-5 top-5 md:left-7 md:top-7", "group fixed z-0 rounded-lg")}
|
||||
className={cn(previewSurvey ? "" : "left-3 top-3 md:left-7 md:top-7", "group fixed z-0 rounded-lg")}
|
||||
style={{ backgroundColor: product.logo?.bgColor }}>
|
||||
{previewSurvey && environmentId && (
|
||||
<Link
|
||||
|
||||
@@ -34,7 +34,6 @@ const CodeBlock: React.FC<CodeBlockProps> = ({
|
||||
{showCopyToClipboard && (
|
||||
<div className="absolute right-2 top-2 z-20 h-8 w-8 cursor-pointer rounded-md bg-slate-100 p-1.5 text-slate-600 hover:bg-slate-200">
|
||||
<CopyIcon
|
||||
className=""
|
||||
onClick={() => {
|
||||
const childText = children?.toString() || "";
|
||||
navigator.clipboard.writeText(childText);
|
||||
|
||||
@@ -203,11 +203,11 @@ export const FileInput: React.FC<FileInputProps> = ({
|
||||
|
||||
return (
|
||||
<div className="w-full cursor-default">
|
||||
<div className="">
|
||||
<div>
|
||||
{isVideoAllowed && (
|
||||
<TabBar tabs={tabs} activeId={activeTab} setActiveId={setActiveTab} tabStyle="button" />
|
||||
)}
|
||||
<div className="">
|
||||
<div>
|
||||
{activeTab === "video" && (
|
||||
<div className={cn(isVideoAllowed && "rounded-b-lg border-x border-b border-slate-200 p-4")}>
|
||||
<VideoSettings
|
||||
|
||||
Reference in New Issue
Block a user