Merge branch 'main' of github.com:formbricks/formbricks into itzabhinavarya/main

This commit is contained in:
Johannes
2023-10-08 16:29:57 +05:30
23 changed files with 784 additions and 372 deletions

View File

@@ -1,5 +1,5 @@
NEXT_PUBLIC_FORMBRICKS_API_HOST=http://localhost:3000
NEXT_PUBLIC_FORMBRICKS_ENVIRONMENT_ID=https://formbricks-formbricks-m4jive9c4tg.ws-us105.gitpod.io/
NEXT_PUBLIC_FORMBRICKS_API_HOST=
NEXT_PUBLIC_FORMBRICKS_ENVIRONMENT_ID=
# Copy the environment ID for the URL of your Formbricks App and
# paste it above to connect your Formbricks App with the Demo App.

View File

@@ -114,7 +114,7 @@ export default function Header() {
<Bars3Icon className="h-6 w-6" aria-hidden="true" />
</Popover.Button>
</div>
<Popover.Group as="nav" className="hidden space-x-10 md:flex">
<Popover.Group as="nav" className="hidden space-x-6 md:flex lg:space-x-10">
<Popover className="relative">
{({ open }) => (
<>
@@ -125,7 +125,7 @@ export default function Header() {
: "text-slate-400 hover:text-slate-900 dark:hover:text-slate-100",
"group inline-flex items-center rounded-md text-base font-medium hover:text-slate-300 focus:outline-none focus:ring-2 focus:ring-teal-500 focus:ring-offset-2 dark:hover:text-slate-50"
)}>
<span>Best Practices</span>
<span className="text-sm lg:text-base">Best Practices</span>
<ChevronDownIcon
className={clsx(
open ? "text-slate-600" : "text-slate-400",
@@ -251,17 +251,17 @@ export default function Header() {
*/}
<Link
href="/pricing"
className="text-base font-medium text-slate-400 hover:text-slate-700 dark:hover:text-slate-300">
className="text-sm font-medium text-slate-400 hover:text-slate-700 dark:hover:text-slate-300 lg:text-base">
Pricing
</Link>
<Link
href="/docs"
className="text-base font-medium text-slate-400 hover:text-slate-700 dark:hover:text-slate-300">
className="text-sm font-medium text-slate-400 hover:text-slate-700 dark:hover:text-slate-300 lg:text-base">
Docs
</Link>
<Link
href="/blog"
className="text-base font-medium text-slate-400 hover:text-slate-700 dark:hover:text-slate-300">
className="text-sm font-medium text-slate-400 hover:text-slate-700 dark:hover:text-slate-300 lg:text-base">
Blog {/* <p className="bg-brand inline rounded-full px-2 text-xs text-white">1</p> */}
</Link>
{/* <Link
@@ -272,15 +272,15 @@ export default function Header() {
<Link
href="/concierge"
className="text-base font-medium text-slate-400 hover:text-slate-700 dark:hover:text-slate-300">
className="text-sm font-medium text-slate-400 hover:text-slate-700 dark:hover:text-slate-300 lg:text-base">
Concierge
</Link>
</Popover.Group>
<div className="hidden flex-1 items-center justify-end md:flex">
<ThemeSelector className="relative z-10 mr-5" />
<ThemeSelector className="relative z-10 mr-2 lg:mr-5" />
<Button
variant="secondary"
className="group px-2"
className="group hidden px-2 lg:block"
href="https://formbricks.com/github"
target="_blank">
<Image
@@ -303,7 +303,7 @@ export default function Header() {
<Button
variant="highlight"
className="ml-2"
className="ml-2 text-xs lg:text-sm"
onClick={() => {
router.push("https://app.formbricks.com");
plausible("NavBar_CTA_Login");

View File

@@ -234,7 +234,7 @@ export default function Navigation({
return (
<>
{product && (
<nav className="top-0 z-10 w-full border-b border-slate-200 bg-white">
<nav className="top-0 w-full border-b border-slate-200 bg-white">
{environment?.type === "development" && (
<div className="h-6 w-full bg-[#A33700] p-0.5 text-center text-sm text-white">
You&apos;re in development mode. Use it to test surveys, actions and attributes.

View File

@@ -8,7 +8,13 @@ import type { TProduct } from "@formbricks/types/v1/product";
import { TSurvey } from "@formbricks/types/v1/surveys";
import { Button } from "@formbricks/ui";
import { ArrowPathRoundedSquareIcon } from "@heroicons/react/24/outline";
import { ComputerDesktopIcon, DevicePhoneMobileIcon } from "@heroicons/react/24/solid";
import {
ArrowsPointingInIcon,
ArrowsPointingOutIcon,
ComputerDesktopIcon,
DevicePhoneMobileIcon,
} from "@heroicons/react/24/solid";
import { Variants, motion } from "framer-motion";
import { useEffect, useRef, useState } from "react";
type TPreviewType = "modal" | "fullwidth" | "email";
@@ -24,6 +30,65 @@ interface PreviewSurveyProps {
let surveyNameTemp;
const previewParentContainerVariant: Variants = {
expanded: {
position: "fixed",
height: "100%",
width: "100%",
backgroundColor: "rgba(0, 0, 0, 0.4)",
backdropFilter: "blur(15px)",
left: 0,
top: 0,
zIndex: 1040,
transition: {
ease: "easeIn",
duration: 0.001,
},
},
shrink: {
display: "none",
position: "fixed",
backgroundColor: "rgba(0, 0, 0, 0.0)",
backdropFilter: "blur(0px)",
transition: {
duration: 0,
},
zIndex: -1,
},
};
const previewScreenVariants: Variants = {
expanded: {
right: "5%",
bottom: "2%",
width: "90%",
height: "90%",
zIndex: 1050,
boxShadow: "0px 4px 5px 4px rgba(169, 169, 169, 0.25)",
transition: {
ease: "easeInOut",
duration: 0.3,
},
},
expanded_with_fixed_positioning: {
zIndex: 1050,
position: "fixed",
right: "5%",
bottom: "5%",
width: "90%",
height: "90%",
transition: {
ease: "easeOut",
duration: 0.2,
},
},
shrink: {
display: "relative",
width: ["83.33%"],
height: ["95%"],
},
};
export default function PreviewSurvey({
setActiveQuestionId,
activeQuestionId,
@@ -33,8 +98,10 @@ export default function PreviewSurvey({
environment,
}: PreviewSurveyProps) {
const [isModalOpen, setIsModalOpen] = useState(true);
const [isFullScreenPreview, setIsFullScreenPreview] = useState(false);
const [widgetSetupCompleted, setWidgetSetupCompleted] = useState(false);
const [previewMode, setPreviewMode] = useState("desktop");
const [previewPosition, setPreviewPosition] = useState("relative");
const ContentRef = useRef<HTMLDivElement | null>(null);
const { productOverwrites } = survey || {};
@@ -62,12 +129,12 @@ export default function PreviewSurvey({
}
}, [activeQuestionId, survey.type, survey, setActiveQuestionId]);
// this useEffect is fo refreshing the survey preview only if user is switching between templates on survey templates page and hence we are checking for survey.id === "someUniqeId1" which is a common Id for all templates
useEffect(() => {
if (survey.name !== surveyNameTemp) {
if (survey.name !== surveyNameTemp && survey.id === "someUniqueId1") {
resetQuestionProgress();
surveyNameTemp = survey.name;
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [survey]);
@@ -99,7 +166,22 @@ export default function PreviewSurvey({
return (
<div className="flex h-full w-full flex-col items-center justify-items-center">
<div className="relative flex h-[95%] max-h-[95%] w-5/6 items-center justify-center rounded-lg border border-slate-300 bg-slate-200">
<motion.div
variants={previewParentContainerVariant}
className="fixed hidden h-[95%] w-5/6"
animate={isFullScreenPreview ? "expanded" : "shrink"}
/>
<motion.div
layout
variants={previewScreenVariants}
animate={
isFullScreenPreview
? previewPosition === "relative"
? "expanded"
: "expanded_with_fixed_positioning"
: "shrink"
}
className="relative flex h-[95] max-h-[95%] w-5/6 items-center justify-center rounded-lg border border-slate-300 bg-slate-200">
{previewMode === "mobile" && (
<>
<div className="absolute right-0 top-0 m-2">
@@ -153,7 +235,26 @@ export default function PreviewSurvey({
</div>
<p className="ml-4 flex w-full justify-between font-mono text-sm text-slate-400">
{previewType === "modal" ? "Your web app" : "Preview"}
<ResetProgressButton resetQuestionProgress={resetQuestionProgress} />
<div className="flex items-center">
{isFullScreenPreview ? (
<ArrowsPointingInIcon
className="mr-2 h-4 w-4 cursor-pointer"
onClick={() => {
setIsFullScreenPreview(false);
setPreviewPosition("relative");
}}
/>
) : (
<ArrowsPointingOutIcon
className="mr-2 h-4 w-4 cursor-pointer"
onClick={() => {
setIsFullScreenPreview(true);
setTimeout(() => setPreviewPosition("fixed"), 300);
}}
/>
)}
<ResetProgressButton resetQuestionProgress={resetQuestionProgress} />
</div>
</p>
</div>
@@ -173,7 +274,7 @@ export default function PreviewSurvey({
/>
</Modal>
) : (
<div className="flex flex-grow flex-col overflow-y-auto" ref={ContentRef}>
<div className="flex flex-grow flex-col overflow-y-auto rounded-b-lg" ref={ContentRef}>
<div className="flex w-full flex-grow flex-col items-center justify-center bg-white p-4 py-6">
<div className="w-full max-w-md">
<SurveyInline
@@ -190,7 +291,8 @@ export default function PreviewSurvey({
)}
</div>
)}
</div>
</motion.div>
{/* for toggling between mobile and desktop mode */}
<div className="mt-2 flex rounded-full border-2 border-slate-300 p-1">
<TabOption

View File

@@ -28,9 +28,9 @@ export const RatingResponse: React.FC<RatingResponseProps> = ({ scale, range, an
const stars: any = [];
for (let i = 0; i < range; i++) {
if (i < parseInt(answer)) {
stars.push(<StarIcon className="h-7 text-yellow-400" />);
stars.push(<StarIcon key={i} className="h-7 text-yellow-400" />);
} else {
stars.push(<StarIcon className="h-7 text-gray-300" />);
stars.push(<StarIcon key={i} className="h-7 text-gray-300" />);
}
}
return <div className="flex">{stars}</div>;

View File

@@ -1,8 +1,20 @@
import { TSurveyOpenTextQuestion, TSurveyWithAnalytics } from "@formbricks/types/v1/surveys";
import { Button, Input, Label } from "@formbricks/ui";
import { TrashIcon, PlusIcon } from "@heroicons/react/24/solid";
import {
TSurveyOpenTextQuestion,
TSurveyOpenTextQuestionInputType,
TSurveyWithAnalytics,
} from "@formbricks/types/v1/surveys";
import { Button, Input, Label, QuestionTypeSelector } from "@formbricks/ui";
import { PlusIcon, TrashIcon } from "@heroicons/react/24/solid";
import { useState } from "react";
const questionTypes = [
{ value: "text", label: "Text" },
{ value: "email", label: "Email" },
{ value: "url", label: "URL" },
{ value: "number", label: "Number" },
{ value: "phone", label: "Phone" },
];
interface OpenQuestionFormProps {
localSurvey: TSurveyWithAnalytics;
question: TSurveyOpenTextQuestion;
@@ -19,6 +31,16 @@ export default function OpenQuestionForm({
isInValid,
}: OpenQuestionFormProps): JSX.Element {
const [showSubheader, setShowSubheader] = useState(!!question.subheader);
const defaultPlaceholder = getPlaceholderByInputType(question.inputType ?? "text");
const handleInputChange = (inputType: TSurveyOpenTextQuestionInputType) => {
const updatedAttributes = {
inputType: inputType,
placeholder: getPlaceholderByInputType(inputType),
longAnswer: inputType === "text" ? question.longAnswer : false,
};
updateQuestion(questionIdx, updatedAttributes);
};
return (
<form>
@@ -71,11 +93,38 @@ export default function OpenQuestionForm({
<Input
id="placeholder"
name="placeholder"
value={question.placeholder}
value={question.placeholder ?? defaultPlaceholder}
onChange={(e) => updateQuestion(questionIdx, { placeholder: e.target.value })}
/>
</div>
</div>
{/* Add a dropdown to select the question type */}
<div className="mt-3">
<Label htmlFor="questionType">Input Type</Label>
<div className="mt-2 flex items-center">
<QuestionTypeSelector
questionTypes={questionTypes}
currentType={question.inputType}
handleTypeChange={handleInputChange} // Use the merged function
/>
</div>
</div>
</form>
);
}
function getPlaceholderByInputType(inputType: TSurveyOpenTextQuestionInputType) {
switch (inputType) {
case "email":
return "example@email.com";
case "url":
return "http://...";
case "number":
return "42";
case "phone":
return "+1 123 456 789";
default:
return "Type your answer here...";
}
}

View File

@@ -86,12 +86,14 @@ export const templates: TTemplate[] = [
type: QuestionType.OpenText,
headline: "What type of people do you think would most benefit from {{productName}}?",
required: true,
inputType: "text",
},
{
id: createId(),
type: QuestionType.OpenText,
headline: "What is the main benefit your receive from {{productName}}?",
required: true,
inputType: "text",
},
{
id: createId(),
@@ -99,6 +101,7 @@ export const templates: TTemplate[] = [
headline: "How can we improve {{productName}} for you?",
subheader: "Please be as specific as possible.",
required: true,
inputType: "text",
},
],
thankYouCard: thankYouCardDefault,
@@ -253,6 +256,7 @@ export const templates: TTemplate[] = [
required: true,
subheader: "",
buttonLabel: "Send",
inputType: "text",
},
{
id: "mao94214zoo6c1at5rpuz7io",
@@ -274,6 +278,7 @@ export const templates: TTemplate[] = [
headline: "What features are you missing?",
required: true,
subheader: "",
inputType: "text",
},
{
id: "hdftsos1odzjllr7flj4m3j9",
@@ -320,6 +325,7 @@ export const templates: TTemplate[] = [
headline: "Great to hear! Why did you recommend us?",
required: true,
placeholder: "Type your answer here...",
inputType: "text",
},
{
id: "duz2qp8eftix9wty1l221x1h",
@@ -327,6 +333,7 @@ export const templates: TTemplate[] = [
headline: "So sad. Why not?",
required: true,
placeholder: "Type your answer here...",
inputType: "text",
},
{
id: "yhfew1j3ng6luy7t7qynwj79",
@@ -347,6 +354,7 @@ export const templates: TTemplate[] = [
headline: "What made you discourage them?",
required: true,
placeholder: "Type your answer here...",
inputType: "text",
},
],
thankYouCard: thankYouCardDefault,
@@ -405,6 +413,7 @@ export const templates: TTemplate[] = [
headline: "Sorry to hear. What was the biggest problem using {{productName}}?",
required: true,
buttonLabel: "Next",
inputType: "text",
},
{
id: "rnrfydttavtsf2t2nfx1df7m",
@@ -413,6 +422,7 @@ export const templates: TTemplate[] = [
headline: "What did you expect {{productName}} would do for you?",
required: true,
buttonLabel: "Next",
inputType: "text",
},
{
id: "x760wga1fhtr1i80cpssr7af",
@@ -434,6 +444,7 @@ export const templates: TTemplate[] = [
required: true,
subheader: "What would you like to achieve?",
buttonLabel: "Next",
inputType: "text",
},
{
id: "bqiyml1ym74ggx6htwdo7rlu",
@@ -445,6 +456,7 @@ export const templates: TTemplate[] = [
headline: "How are you solving your problem now?",
required: false,
subheader: "Please name alternative solutions:",
inputType: "text",
},
],
thankYouCard: thankYouCardDefault,
@@ -490,6 +502,7 @@ export const templates: TTemplate[] = [
subheader: "Help us improve your experience.",
buttonLabel: "Send",
placeholder: "Type your answer here...",
inputType: "text",
},
],
thankYouCard: thankYouCardDefault,
@@ -567,6 +580,7 @@ export const templates: TTemplate[] = [
required: true,
subheader: "",
placeholder: "Type your answer here...",
inputType: "text",
},
{
id: "r0zvi3vburf4hm7qewimzjux",
@@ -576,6 +590,7 @@ export const templates: TTemplate[] = [
required: true,
subheader: "",
placeholder: "Type your answer here...",
inputType: "text",
},
{
id: "rbwz3y6y9avzqcfj30nu0qj4",
@@ -585,6 +600,7 @@ export const templates: TTemplate[] = [
required: true,
subheader: "",
placeholder: "Type your answer here...",
inputType: "text",
},
{
id: "gn6298zogd2ipdz7js17qy5i",
@@ -594,6 +610,7 @@ export const templates: TTemplate[] = [
required: true,
subheader: "",
placeholder: "Type your answer here...",
inputType: "text",
},
{
id: "c0exdyri3erugrv0ezkyseh6",
@@ -603,6 +620,7 @@ export const templates: TTemplate[] = [
required: false,
subheader: "We're eager to fix it asap.",
placeholder: "Type your answer here...",
inputType: "text",
},
],
thankYouCard: thankYouCardDefault,
@@ -651,6 +669,7 @@ export const templates: TTemplate[] = [
headline: "Would you like to add something?",
required: false,
subheader: "Feel free to speak your mind, we do too.",
inputType: "text",
},
],
thankYouCard: thankYouCardDefault,
@@ -691,6 +710,7 @@ export const templates: TTemplate[] = [
headline: "How can we improve {{productName}} for you?",
subheader: "Please be as specific as possible.",
required: true,
inputType: "text",
},
],
thankYouCard: thankYouCardDefault,
@@ -959,6 +979,7 @@ export const templates: TTemplate[] = [
headline: "What's broken?",
required: true,
subheader: "The more detail, the better :)",
inputType: "text",
},
{
id: "a6c76m5oocw6xp9agf3d2tam",
@@ -982,6 +1003,7 @@ export const templates: TTemplate[] = [
subheader: "What problem do you want us to solve?",
buttonLabel: "Request feature",
placeholder: "Type your answer here...",
inputType: "text",
},
],
thankYouCard: thankYouCardDefault,
@@ -1014,6 +1036,7 @@ export const templates: TTemplate[] = [
headline: "Why was it hard?",
required: false,
placeholder: "Type your answer here...",
inputType: "text",
},
{
id: "ef0qo3l8iisd517ikp078u1p",
@@ -1021,6 +1044,7 @@ export const templates: TTemplate[] = [
headline: "What other tools would you like to use with {{productName}}?",
required: false,
subheader: "We keep building integrations, yours can be next:",
inputType: "text",
},
],
thankYouCard: thankYouCardDefault,
@@ -1096,12 +1120,14 @@ export const templates: TTemplate[] = [
type: QuestionType.OpenText,
headline: "Please elaborate:",
required: false,
inputType: "text",
},
{
id: createId(),
type: QuestionType.OpenText,
headline: "Page URL",
required: false,
inputType: "text",
},
],
thankYouCard: thankYouCardDefault,
@@ -1129,6 +1155,7 @@ export const templates: TTemplate[] = [
type: QuestionType.OpenText,
headline: "What made you give that rating?",
required: false,
inputType: "text",
},
],
thankYouCard: thankYouCardDefault,
@@ -1162,6 +1189,7 @@ export const templates: TTemplate[] = [
headline: "Lovely! Is there anything we can do to improve your experience?",
required: false,
placeholder: "Type your answer here...",
inputType: "text",
},
{
id: "vyo4mkw4ln95ts4ya7qp2tth",
@@ -1169,6 +1197,7 @@ export const templates: TTemplate[] = [
headline: "Ugh, sorry! Is there anything we can do to improve your experience?",
required: false,
placeholder: "Type your answer here...",
inputType: "text",
},
],
thankYouCard: thankYouCardDefault,
@@ -1257,6 +1286,7 @@ export const templates: TTemplate[] = [
headline: "How else could we improve you experience with {{productName}}?",
required: true,
placeholder: "Type your answer here...",
inputType: "text",
},
],
thankYouCard: thankYouCardDefault,
@@ -1286,6 +1316,7 @@ export const templates: TTemplate[] = [
type: QuestionType.OpenText,
headline: "What is one thing we could do better?",
required: false,
inputType: "text",
},
],
thankYouCard: thankYouCardDefault,
@@ -1326,6 +1357,7 @@ export const templates: TTemplate[] = [
type: QuestionType.OpenText,
headline: "Whats missing or unclear to you about {{productName}}?",
required: false,
inputType: "text",
},
{
id: createId(),
@@ -1366,6 +1398,7 @@ export const templates: TTemplate[] = [
headline: "Thanks! How could we make it easier for you to [ADD GOAL]?",
required: true,
placeholder: "Type your answer here...",
inputType: "text",
},
],
thankYouCard: thankYouCardDefault,
@@ -1400,6 +1433,7 @@ export const templates: TTemplate[] = [
headline: "Sorry about that! What would have made it easier for you?",
required: true,
placeholder: "Type your answer here...",
inputType: "text",
},
{
id: "lpof3d9t9hmnqvyjlpksmxd7",
@@ -1407,6 +1441,7 @@ export const templates: TTemplate[] = [
headline: "Lovely! Is there anything we can do to improve your experience?",
required: true,
placeholder: "Type your answer here...",
inputType: "text",
},
],
thankYouCard: thankYouCardDefault,
@@ -1440,6 +1475,7 @@ export const templates: TTemplate[] = [
headline: "Ugh! What makes the results irrelevant for you?",
required: true,
placeholder: "Type your answer here...",
inputType: "text",
},
{
id: "adcs3d9t9hmnqvyjlpksmxd7",
@@ -1447,6 +1483,7 @@ export const templates: TTemplate[] = [
headline: "Lovely! Is there anything we can do to improve your experience?",
required: true,
placeholder: "Type your answer here...",
inputType: "text",
},
],
thankYouCard: thankYouCardDefault,
@@ -1480,6 +1517,7 @@ export const templates: TTemplate[] = [
headline: "Hmpft! What were you hoping for?",
required: true,
placeholder: "Type your answer here...",
inputType: "text",
},
{
id: "adcs3d9t9hmnqvyjlpkswi38",
@@ -1487,6 +1525,7 @@ export const templates: TTemplate[] = [
headline: "Lovely! Is there anything else you would like us to cover?",
required: true,
placeholder: "Topics, trends, tutorials...",
inputType: "text",
},
],
thankYouCard: thankYouCardDefault,
@@ -1539,6 +1578,7 @@ export const templates: TTemplate[] = [
headline: "What made it hard?",
required: false,
placeholder: "Type your answer here...",
inputType: "text",
},
{
id: "nq88udm0jjtylr16ax87xlyc",
@@ -1550,6 +1590,7 @@ export const templates: TTemplate[] = [
headline: "Great! What did you come here to do today?",
required: false,
buttonLabel: "Send",
inputType: "text",
},
{
id: "u83zhr66knyfozccoqojx7bc",
@@ -1558,6 +1599,7 @@ export const templates: TTemplate[] = [
required: true,
buttonLabel: "Send",
placeholder: "Type your answer here...",
inputType: "text",
},
],
thankYouCard: thankYouCardDefault,
@@ -1632,6 +1674,7 @@ export const templates: TTemplate[] = [
headline: "What do you need but {{productName}} does not offer?",
required: true,
placeholder: "Type your answer here...",
inputType: "text",
},
{
id: "j7jkpolm5xl7u0zt3g0e4z7d",
@@ -1640,6 +1683,7 @@ export const templates: TTemplate[] = [
headline: "What options are you looking at?",
required: true,
placeholder: "Type your answer here...",
inputType: "text",
},
{
id: "t5gvag2d7kq311szz5iyiy79",
@@ -1648,6 +1692,7 @@ export const templates: TTemplate[] = [
headline: "What seems complicated to you?",
required: true,
placeholder: "Type your answer here...",
inputType: "text",
},
{
id: "or0yhhrof753sq9ug4mdavgz",
@@ -1656,6 +1701,7 @@ export const templates: TTemplate[] = [
headline: "What are you concerned about regarding pricing?",
required: true,
placeholder: "Type your answer here...",
inputType: "text",
},
{
id: "v0pq1qcnm6ohiry5ywcd91qq",
@@ -1663,6 +1709,7 @@ export const templates: TTemplate[] = [
headline: "Please explain:",
required: true,
placeholder: "Type your answer here...",
inputType: "text",
},
{
id: "k3q0vt1ko0bzbsq076p7lnys",
@@ -1706,6 +1753,7 @@ export const templates: TTemplate[] = [
required: true,
subheader: "",
placeholder: "Type your answer here...",
inputType: "text",
},
],
thankYouCard: thankYouCardDefault,
@@ -1747,6 +1795,7 @@ export const templates: TTemplate[] = [
headline: "Got it. What's your primary reason for visiting today?",
required: false,
placeholder: "Type your answer here...",
inputType: "text",
},
{
id: "zm1hs8qkeuidh3qm0hx8pnw7",
@@ -1754,6 +1803,7 @@ export const templates: TTemplate[] = [
headline: "What, if anything, is holding you back from making a purchase today?",
required: true,
placeholder: "Type your answer here...",
inputType: "text",
},
],
thankYouCard: thankYouCardDefault,
@@ -1793,6 +1843,7 @@ export const templates: TTemplate[] = [
headline: "What would have made this weeks newsletter more helpful?",
required: false,
placeholder: "Type your answer here...",
inputType: "text",
},
{
id: "l2q1chqssong8n0xwaagyl8g",
@@ -1851,6 +1902,7 @@ export const templates: TTemplate[] = [
required: true,
subheader: "",
placeholder: "Type your answer here...",
inputType: "text",
},
{
id: "jmzgbo73cfjswlvhoynn7o0q",
@@ -1884,6 +1936,7 @@ export const templates: TTemplate[] = [
headline: "Got it. Why wouldn't this feature be valuable to you?",
required: true,
placeholder: "Type your answer here...",
inputType: "text",
},
{
id: "gvzevzw4hkqd6dmlkcly6kd1",
@@ -1891,6 +1944,7 @@ export const templates: TTemplate[] = [
headline: "Got it. What would be most valuable to you in this feature?",
required: true,
placeholder: "Type your answer here...",
inputType: "text",
},
{
id: "bqmnpyku9etsgbtb322luzb2",
@@ -1898,6 +1952,7 @@ export const templates: TTemplate[] = [
headline: "Anything else we should keep in mind?",
required: false,
placeholder: "Type your answer here...",
inputType: "text",
},
],
thankYouCard: thankYouCardDefault,
@@ -1954,6 +2009,7 @@ export const templates: TTemplate[] = [
required: true,
subheader: "",
placeholder: "Type your answer here...",
inputType: "text",
},
{
id: "g92s5wetp51ps6afmc6y7609",
@@ -1963,6 +2019,7 @@ export const templates: TTemplate[] = [
required: true,
subheader: "",
placeholder: "Type your answer here...",
inputType: "text",
},
{
id: "gn6298zogd2ipdz7js17qy5i",
@@ -1972,6 +2029,7 @@ export const templates: TTemplate[] = [
required: true,
subheader: "",
placeholder: "Type your answer here...",
inputType: "text",
},
{
id: "rbwz3y6y9avzqcfj30nu0qj4",
@@ -1981,6 +2039,7 @@ export const templates: TTemplate[] = [
required: true,
subheader: "",
placeholder: "Type your answer here...",
inputType: "text",
},
{
id: "c0exdyri3erugrv0ezkyseh6",
@@ -1990,6 +2049,7 @@ export const templates: TTemplate[] = [
required: false,
subheader: "",
placeholder: "Type your answer here...",
inputType: "text",
},
],
thankYouCard: thankYouCardDefault,
@@ -2032,6 +2092,7 @@ export const customSurvey: TTemplate = {
subheader: "This is an example survey.",
placeholder: "Type your answer here...",
required: true,
inputType: "text",
},
],
thankYouCard: thankYouCardDefault,

View File

@@ -32,7 +32,8 @@
"@t3-oss/env-nextjs": "^0.7.0",
"bcryptjs": "^2.4.3",
"encoding": "^0.1.13",
"eslint-config-next": "^13.5.4",
"eslint-config-next": "^13.5.3",
"framer-motion": "10.16.4",
"googleapis": "^126.0.1",
"jsonwebtoken": "^9.0.2",
"lodash": "^4.17.21",

View File

@@ -33,8 +33,8 @@
"eslint-config-formbricks": "workspace:*",
"husky": "^8.0.3",
"lint-staged": "^14.0.1",
"rimraf": "^5.0.1",
"tsx": "^3.12.7",
"rimraf": "^5.0.5",
"tsx": "^3.13.0",
"turbo": "latest"
},
"lint-staged": {

View File

@@ -3,11 +3,13 @@ import { cn } from "../../../lib/cn";
interface BackButtonProps {
onClick: () => void;
backButtonLabel?: string;
tabIndex?: number;
}
export function BackButton({ onClick, backButtonLabel }: BackButtonProps) {
export function BackButton({ onClick, backButtonLabel, tabIndex = 2 }: BackButtonProps) {
return (
<button
tabIndex={tabIndex}
type={"button"}
className={cn(
"flex items-center rounded-md border border-transparent px-3 py-3 text-base font-medium leading-4 shadow-sm hover:opacity-90 focus:outline-none focus:ring-2 focus:ring-slate-500 focus:ring-offset-2"

View File

@@ -33,9 +33,10 @@ export default function CTAQuestion({
{!isFirstQuestion && (
<BackButton backButtonLabel={question.backButtonLabel} onClick={() => onBack()} />
)}
<div className="flex justify-end">
<div className="flex w-full justify-end">
{!question.required && (
<button
tabIndex={0}
type="button"
onClick={() => {
onSubmit({ [question.id]: "dismissed" });
@@ -48,6 +49,7 @@ export default function CTAQuestion({
question={question}
isLastQuestion={isLastQuestion}
brandColor={brandColor}
focus={true}
onClick={() => {
if (question.buttonExternal && question.buttonUrl) {
window?.open(question.buttonUrl, "_blank")?.focus();

View File

@@ -36,7 +36,14 @@ export default function ConsentQuestion({
e.preventDefault();
onSubmit({ [question.id]: value });
}}>
<label className="relative z-10 mt-4 flex w-full cursor-pointer items-center rounded-md border border-gray-200 bg-slate-50 p-4 text-sm text-slate-800 focus:outline-none">
<label
tabIndex={1}
onKeyDown={(e) => {
if (e.key == "Enter") {
onChange({ [question.id]: "accepted" });
}
}}
className="relative z-10 mt-4 flex w-full cursor-pointer items-center rounded-md border border-gray-200 p-4 text-sm text-slate-800 hover:bg-slate-50 focus:bg-slate-50 focus:outline-none focus:ring-2 focus:ring-slate-500 focus:ring-offset-2">
<input
type="checkbox"
id={question.id}
@@ -62,10 +69,11 @@ export default function ConsentQuestion({
<div className="mt-4 flex w-full justify-between">
{!isFirstQuestion && (
<BackButton backButtonLabel={question.backButtonLabel} onClick={() => onBack()} />
<BackButton tabIndex={3} backButtonLabel={question.backButtonLabel} onClick={() => onBack()} />
)}
<div />
<SubmitButton
tabIndex={2}
brandColor={brandColor}
question={question}
isLastQuestion={isLastQuestion}

View File

@@ -3,6 +3,7 @@ export default function FormbricksSignature() {
<a
href="https://formbricks.com?utm_source=survey_branding"
target="_blank"
tabIndex={-1}
className="mb-5 mt-2 flex justify-center">
<p className="text-xs text-slate-400">
Powered by{" "}

View File

@@ -86,18 +86,29 @@ export default function MultipleChoiceSingleQuestion({
<fieldset>
<legend className="sr-only">Options</legend>
<div className="relative max-h-[42vh] space-y-2 overflow-y-auto rounded-md bg-white py-0.5 pr-2">
{questionChoices.map((choice) => (
{questionChoices.map((choice, idx) => (
<label
key={choice.id}
tabIndex={idx + 1}
onKeyDown={(e) => {
if (e.key == "Enter") {
if (Array.isArray(value) && value.includes(choice.label)) {
removeItem(choice.label);
} else {
addItem(choice.label);
}
}
}}
className={cn(
value === choice.label ? "z-10 border-slate-400 bg-slate-50" : "border-gray-200",
"relative flex cursor-pointer flex-col rounded-md border p-4 text-slate-800 hover:bg-slate-50 focus:outline-none"
"relative flex cursor-pointer flex-col rounded-md border p-4 text-slate-800 focus-within:border-slate-400 hover:bg-slate-50 focus:bg-slate-50 focus:outline-none "
)}>
<span className="flex items-center text-sm">
<input
type="checkbox"
id={choice.id}
name={question.id}
tabIndex={-1}
value={choice.label}
className="h-4 w-4 border border-slate-300 focus:ring-0 focus:ring-offset-0"
aria-labelledby={`${choice.id}-label`}
@@ -110,9 +121,7 @@ export default function MultipleChoiceSingleQuestion({
}}
checked={Array.isArray(value) && value.includes(choice.label)}
style={{ borderColor: brandColor, color: brandColor }}
required={
question.required && Array.isArray(value) && value.length ? false : question.required
}
required={question.required && idx === 0}
/>
<span id={`${choice.id}-label`} className="ml-3 font-medium">
{choice.label}
@@ -122,13 +131,20 @@ export default function MultipleChoiceSingleQuestion({
))}
{otherOption && (
<label
tabIndex={questionChoices.length + 1}
className={cn(
value === otherOption.label ? "z-10 border-slate-400 bg-slate-50" : "border-gray-200",
"relative flex cursor-pointer flex-col rounded-md border p-4 text-slate-800 hover:bg-slate-50 focus:outline-none"
)}>
"relative flex cursor-pointer flex-col rounded-md border p-4 text-slate-800 focus-within:border-slate-400 focus-within:bg-slate-50 hover:bg-slate-50 focus:outline-none"
)}
onKeyDown={(e) => {
if (e.key == "Enter") {
setOtherSelected(!otherSelected);
}
}}>
<span className="flex items-center text-sm">
<input
type="checkbox"
tabIndex={-1}
id={otherOption.id}
name={question.id}
value={otherOption.label}
@@ -155,12 +171,20 @@ export default function MultipleChoiceSingleQuestion({
ref={otherSpecify}
id={`${otherOption.id}-label`}
name={question.id}
tabIndex={questionChoices.length + 1}
value={otherValue}
onChange={(e) => {
setOtherValue(e.currentTarget.value);
removeItem(otherValue);
addItem(e.currentTarget.value);
}}
onKeyDown={(e) => {
if (e.key == "Enter") {
setTimeout(() => {
onSubmit({ [question.id]: value });
}, 100);
}
}}
placeholder="Please specify"
className="mt-3 flex h-10 w-full rounded-md border border-slate-300 bg-transparent bg-white px-3 py-2 text-sm text-slate-800 placeholder:text-slate-400 focus:outline-none focus:ring-2 focus:ring-slate-400 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 dark:border-slate-500 dark:text-slate-300"
required={question.required}
@@ -173,9 +197,16 @@ export default function MultipleChoiceSingleQuestion({
</fieldset>
</div>
<div className="mt-4 flex w-full justify-between">
{!isFirstQuestion && <BackButton backButtonLabel={question.backButtonLabel} onClick={onBack} />}
{!isFirstQuestion && (
<BackButton
tabIndex={questionChoices.length + 3}
backButtonLabel={question.backButtonLabel}
onClick={onBack}
/>
)}
<div></div>
<SubmitButton
tabIndex={questionChoices.length + 2}
question={question}
isLastQuestion={isLastQuestion}
brandColor={brandColor}

View File

@@ -55,7 +55,6 @@ export default function MultipleChoiceSingleQuestion({
otherSpecify.current?.focus();
}
}, [otherSelected]);
return (
<form
onSubmit={(e) => {
@@ -68,16 +67,28 @@ export default function MultipleChoiceSingleQuestion({
<div className="mt-4">
<fieldset>
<legend className="sr-only">Options</legend>
<div className="relative max-h-[42vh] space-y-2 overflow-y-auto rounded-md bg-white py-0.5 pr-2">
<div
className="relative max-h-[42vh] space-y-2 overflow-y-auto rounded-md bg-white py-0.5 pr-2"
role="radiogroup">
{questionChoices.map((choice, idx) => (
<label
key={choice.id}
tabIndex={idx + 1}
onKeyDown={(e) => {
if (e.key == "Enter") {
onChange({ [question.id]: choice.label });
setTimeout(() => {
onSubmit({ [question.id]: choice.label });
}, 350);
}
}}
className={cn(
value === choice.label ? "z-10 border-slate-400 bg-slate-50" : "border-gray-200",
"relative flex cursor-pointer flex-col rounded-md border p-4 text-slate-800 hover:bg-slate-50 focus:outline-none"
"relative flex cursor-pointer flex-col rounded-md border p-4 text-slate-800 focus-within:border-slate-400 focus-within:bg-slate-50 hover:bg-slate-50 focus:outline-none "
)}>
<span className="flex items-center text-sm">
<input
tabIndex={-1}
type="radio"
id={choice.id}
name={question.id}
@@ -100,14 +111,22 @@ export default function MultipleChoiceSingleQuestion({
))}
{otherOption && (
<label
tabIndex={questionChoices.length + 1}
className={cn(
value === otherOption.label ? "z-10 border-slate-400 bg-slate-50" : "border-gray-200",
"relative flex cursor-pointer flex-col rounded-md border p-4 text-slate-800 hover:bg-slate-50 focus:outline-none"
)}>
"relative flex cursor-pointer flex-col rounded-md border p-4 text-slate-800 focus-within:border-slate-400 focus-within:bg-slate-50 hover:bg-slate-50 focus:outline-none"
)}
onKeyDown={(e) => {
if (e.key == "Enter") {
setOtherSelected(!otherSelected);
if (!otherSelected) onChange({ [question.id]: "" });
}
}}>
<span className="flex items-center text-sm">
<input
type="radio"
id={otherOption.id}
tabIndex={-1}
name={question.id}
value={otherOption.label}
className="h-4 w-4 border border-slate-300 focus:ring-0 focus:ring-offset-0"
@@ -126,12 +145,20 @@ export default function MultipleChoiceSingleQuestion({
{otherSelected && (
<input
ref={otherSpecify}
tabIndex={questionChoices.length + 1}
id={`${otherOption.id}-label`}
name={question.id}
value={value}
onChange={(e) => {
onChange({ [question.id]: e.currentTarget.value });
}}
onKeyDown={(e) => {
if (e.key == "Enter") {
setTimeout(() => {
onSubmit({ [question.id]: value });
}, 100);
}
}}
placeholder="Please specify"
className="mt-3 flex h-10 w-full rounded-md border border-slate-300 bg-transparent bg-white px-3 py-2 text-sm text-slate-800 placeholder:text-slate-400 focus:outline-none focus:ring-2 focus:ring-slate-400 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 dark:border-slate-500 dark:text-slate-300"
required={question.required}
@@ -144,9 +171,16 @@ export default function MultipleChoiceSingleQuestion({
</fieldset>
</div>
<div className="mt-4 flex w-full justify-between">
{!isFirstQuestion && <BackButton backButtonLabel={question.backButtonLabel} onClick={onBack} />}
{!isFirstQuestion && (
<BackButton
backButtonLabel={question.backButtonLabel}
tabIndex={questionChoices.length + 3}
onClick={onBack}
/>
)}
<div></div>
<SubmitButton
tabIndex={questionChoices.length + 2}
question={question}
isLastQuestion={isLastQuestion}
brandColor={brandColor}

View File

@@ -39,12 +39,18 @@ export default function NPSQuestion({
<fieldset>
<legend className="sr-only">Options</legend>
<div className="flex">
{Array.from({ length: 11 }, (_, i) => i).map((number) => (
{Array.from({ length: 11 }, (_, i) => i).map((number, idx) => (
<label
key={number}
tabIndex={idx + 1}
onKeyDown={(e) => {
if (e.key == "Enter") {
onSubmit({ [question.id]: number });
}
}}
className={cn(
value === number ? "z-10 border-slate-400 bg-slate-50" : "",
"relative h-10 flex-1 cursor-pointer border bg-white text-center text-sm leading-10 text-slate-800 first:rounded-l-md last:rounded-r-md hover:bg-gray-100 focus:outline-none"
"relative h-10 flex-1 cursor-pointer border bg-white text-center text-sm leading-10 text-slate-800 first:rounded-l-md last:rounded-r-md hover:bg-gray-100 focus:bg-gray-100 focus:outline-none"
)}>
<input
type="radio"
@@ -76,6 +82,7 @@ export default function NPSQuestion({
<div className="mt-4 flex w-full justify-between">
{!isFirstQuestion && (
<BackButton
tabIndex={isLastQuestion ? 12 : 13}
backButtonLabel={question.backButtonLabel}
onClick={() => {
onBack();
@@ -85,6 +92,7 @@ export default function NPSQuestion({
<div></div>
{!question.required && (
<SubmitButton
tabIndex={12}
question={question}
isLastQuestion={isLastQuestion}
brandColor={brandColor}

View File

@@ -4,6 +4,7 @@ import { BackButton } from "./BackButton";
import Headline from "./Headline";
import Subheader from "./Subheader";
import SubmitButton from "./SubmitButton";
import { useCallback } from "react";
interface OpenTextQuestionProps {
question: TSurveyOpenTextQuestion;
@@ -28,11 +29,24 @@ export default function OpenTextQuestion({
brandColor,
autoFocus = true,
}: OpenTextQuestionProps) {
const handleInputChange = (inputValue: string) => {
// const isValidInput = validateInput(inputValue, question.inputType, question.required);
// setIsValid(isValidInput);
onChange({ [question.id]: inputValue });
};
const openTextRef = useCallback((currentElement: HTMLInputElement | HTMLTextAreaElement | null) => {
if (currentElement && autoFocus) {
currentElement.focus();
}
}, []);
return (
<form
onSubmit={(e) => {
e.preventDefault();
onSubmit({ [question.id]: value });
// if ( validateInput(value as string, question.inputType, question.required)) {
onSubmit({ [question.id]: value, inputType: question.inputType });
// }
}}
className="w-full">
<Headline headline={question.headline} questionId={question.id} required={question.required} />
@@ -40,32 +54,46 @@ export default function OpenTextQuestion({
<div className="mt-4">
{question.longAnswer === false ? (
<input
ref={openTextRef}
tabIndex={1}
name={question.id}
id={question.id}
placeholder={question.placeholder}
required={question.required}
value={value}
onInput={(e) => {
onChange({ [question.id]: e.currentTarget.value });
}}
value={value as string}
type={question.inputType}
onInput={(e) => handleInputChange(e.currentTarget.value)}
autoFocus={autoFocus}
className="block w-full rounded-md border border-slate-100 bg-slate-50 p-2 shadow-sm focus:border-slate-500 focus:outline-none focus:ring-0 sm:text-sm"
onKeyDown={(e) => {
if (e.key == "Enter") onSubmit({ [question.id]: value });
}}
pattern={question.inputType === "phone" ? "[+][0-9 ]+" : ".*"}
title={question.inputType === "phone" ? "Enter a valid phone number" : undefined}
className={`block w-full rounded-md border
border-slate-100
bg-slate-50 p-2 shadow-sm focus:border-slate-500 focus:outline-none focus:ring-0 sm:text-sm`}
/>
) : (
<textarea
ref={openTextRef}
rows={3}
name={question.id}
tabIndex={1}
id={question.id}
placeholder={question.placeholder}
required={question.required}
value={value}
onInput={(e) => {
onChange({ [question.id]: e.currentTarget.value });
}}
value={value as string}
type={question.inputType}
onInput={(e) => handleInputChange(e.currentTarget.value)}
autoFocus={autoFocus}
className="block w-full rounded-md border border-slate-100 bg-slate-50 p-2 shadow-sm focus:border-slate-500 focus:ring-0 sm:text-sm"></textarea>
pattern={question.inputType === "phone" ? "[+][0-9 ]+" : ".*"}
title={question.inputType === "phone" ? "Please enter a valid phone number" : undefined}
className={`block w-full rounded-md border
border-slate-100
bg-slate-50 p-2 shadow-sm focus:border-slate-500 focus:outline-none focus:ring-0 sm:text-sm`}></textarea>
)}
</div>
<div className="mt-4 flex w-full justify-between">
{!isFirstQuestion && (
<BackButton

View File

@@ -81,24 +81,38 @@ export default function RatingQuestion({
key={number}
onMouseOver={() => setHoveredNumber(number)}
onMouseLeave={() => setHoveredNumber(0)}
className="max-w-10 relative flex max-h-10 flex-1 cursor-pointer justify-center bg-white text-center text-sm leading-10">
className="max-w-10 relative max-h-10 flex-1 cursor-pointer bg-white text-center text-sm leading-10">
{question.scale === "number" ? (
<label
tabIndex={i + 1}
onKeyDown={(e) => {
if (e.key == "Enter") {
handleSelect(number);
}
}}
className={cn(
value === number ? "z-10 border-slate-400 bg-slate-50" : "",
a.length === number ? "rounded-r-md" : "",
number === 1 ? "rounded-l-md" : "",
"block h-full w-full border text-slate-800 hover:bg-gray-100 focus:outline-none"
"block h-full w-full border text-slate-800 hover:bg-gray-100 focus:bg-gray-100 focus:outline-none"
)}>
<HiddenRadioInput number={number} />
{number}
</label>
) : question.scale === "star" ? (
<label
tabIndex={i + 1}
onKeyDown={(e) => {
if (e.key == "Enter") {
handleSelect(number);
}
}}
className={cn(
number <= hoveredNumber ? "text-yellow-500" : "",
"flex h-full w-full justify-center"
)}>
"flex h-full w-full justify-center focus:text-yellow-500 focus:outline-none"
)}
onFocus={() => setHoveredNumber(number)}
onBlur={() => setHoveredNumber(0)}>
<HiddenRadioInput number={number} />
{typeof value === "number" && value >= number ? (
<span className="text-yellow-300">
@@ -132,10 +146,15 @@ export default function RatingQuestion({
</label>
) : (
<label
className={cn(
"flex items-center justify-center text-slate-800",
question.range === 10 ? "h-6 w-6" : "h-full w-full"
)}>
tabIndex={i + 1}
onKeyDown={(e) => {
if (e.key == "Enter") {
handleSelect(number);
}
}}
className="flex h-full w-full justify-center text-slate-800 focus:outline-none"
onFocus={() => setHoveredNumber(number)}
onBlur={() => setHoveredNumber(0)}>
<HiddenRadioInput number={number} />
<RatingSmiley
active={value === number || hoveredNumber === number}
@@ -157,6 +176,7 @@ export default function RatingQuestion({
<div className="mt-4 flex w-full justify-between">
{!isFirstQuestion && (
<BackButton
tabIndex={!question.required || value ? question.range + 2 : question.range + 1}
backButtonLabel={question.backButtonLabel}
onClick={() => {
onBack();
@@ -166,6 +186,7 @@ export default function RatingQuestion({
<div></div>
{(!question.required || value) && (
<SubmitButton
tabIndex={question.range + 1}
question={question}
isLastQuestion={isLastQuestion}
brandColor={brandColor}

View File

@@ -1,3 +1,4 @@
import { useCallback } from "preact/hooks";
import { cn } from "../../../lib/cn";
import { isLight } from "../lib/utils";
import { TSurveyQuestion } from "../../../types/v1/surveys";
@@ -7,13 +8,34 @@ interface SubmitButtonProps {
isLastQuestion: boolean;
brandColor: string;
onClick: () => void;
focus?: boolean;
tabIndex?: number;
type?: "submit" | "button";
}
function SubmitButton({ question, isLastQuestion, brandColor, onClick, type = "submit" }: SubmitButtonProps) {
function SubmitButton({
question,
isLastQuestion,
brandColor,
onClick,
tabIndex = 1,
focus = false,
type = "submit",
}: SubmitButtonProps) {
const buttonRef = useCallback((currentButton: HTMLButtonElement | null) => {
if (currentButton && focus) {
setTimeout(() => {
currentButton.focus();
}, 200);
}
}, []);
return (
<button
ref={buttonRef}
type={type}
tabIndex={tabIndex}
autoFocus={focus}
className={cn(
"flex items-center rounded-md border border-transparent px-3 py-3 text-base font-medium leading-4 shadow-sm hover:opacity-90 focus:outline-none focus:ring-2 focus:ring-slate-500 focus:ring-offset-2",
isLight(brandColor) ? "text-black" : "text-white"

View File

@@ -162,11 +162,15 @@ const ZSurveyQuestionBase = z.object({
isDraft: z.boolean().optional(),
});
export const ZSurveyOpenTextQuestionInputType = z.enum(["text", "email", "url", "number", "phone"]);
export type TSurveyOpenTextQuestionInputType = z.infer<typeof ZSurveyOpenTextQuestionInputType>;
export const ZSurveyOpenTextQuestion = ZSurveyQuestionBase.extend({
type: z.literal(QuestionType.OpenText),
placeholder: z.string().optional(),
longAnswer: z.boolean().optional(),
logic: z.array(ZSurveyOpenTextLogic).optional(),
inputType: ZSurveyOpenTextQuestionInputType.optional().default("text"),
});
export type TSurveyOpenTextQuestion = z.infer<typeof ZSurveyOpenTextQuestion>;

View File

@@ -0,0 +1,53 @@
import {
ChatBubbleBottomCenterTextIcon,
EnvelopeIcon,
HashtagIcon,
LinkIcon,
PhoneIcon,
} from "@heroicons/react/24/solid";
import React from "react";
interface QuestionType {
value: string;
label: string;
}
interface QuestionTypeSelectorProps {
questionTypes: QuestionType[];
currentType: string | undefined;
handleTypeChange: (value: string) => void;
}
const typeIcons: { [key: string]: React.ReactNode } = {
text: <ChatBubbleBottomCenterTextIcon />,
email: <EnvelopeIcon />,
url: <LinkIcon />,
number: <HashtagIcon />,
phone: <PhoneIcon />,
};
export function QuestionTypeSelector({
questionTypes,
currentType,
handleTypeChange,
}: QuestionTypeSelectorProps): JSX.Element {
return (
<div className="flex w-full items-center justify-between rounded-md border p-1">
{questionTypes.map((type) => (
<div
key={type.value}
onClick={() => handleTypeChange(type.value)}
className={`flex-grow cursor-pointer rounded-md bg-${
(currentType === undefined && type.value === "text") || currentType === type.value
? "slate-100"
: "white"
} p-2 text-center`}>
<div className="flex items-center justify-center space-x-2">
<span className="text-sm text-slate-900">{type.label}</span>
<div className="h-4 w-4 text-slate-600 hover:text-slate-800">{typeIcons[type.value]}</div>
</div>
</div>
))}
</div>
);
}

View File

@@ -74,6 +74,7 @@ export { Switch } from "./components/Switch";
export { TabBar } from "./components/TabBar";
export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./components/Tooltip";
export { AddVariablesDropdown, Editor } from "./components/editor";
export { QuestionTypeSelector } from "./components/QuestionTypeSelector";
/* Icons */
export { AngryBirdRage2Icon } from "./components/icons/AngryBirdRage2Icon";

592
pnpm-lock.yaml generated
View File

@@ -21,14 +21,14 @@ importers:
specifier: ^14.0.1
version: 14.0.1
rimraf:
specifier: ^5.0.1
version: 5.0.1
specifier: ^5.0.5
version: 5.0.5
tsx:
specifier: ^3.12.7
version: 3.12.7
specifier: ^3.13.0
version: 3.13.0
turbo:
specifier: latest
version: 1.10.15
version: 1.10.12
apps/demo:
dependencies:
@@ -287,8 +287,11 @@ importers:
specifier: ^0.1.13
version: 0.1.13
eslint-config-next:
specifier: ^13.5.4
specifier: ^13.5.3
version: 13.5.4(eslint@8.50.0)(typescript@5.2.2)
framer-motion:
specifier: 10.16.4
version: 10.16.4(react-dom@18.2.0)(react@18.2.0)
googleapis:
specifier: ^126.0.1
version: 126.0.1(encoding@0.1.13)
@@ -456,7 +459,7 @@ importers:
version: 9.0.0(eslint@8.50.0)
eslint-config-turbo:
specifier: latest
version: 1.10.15(eslint@8.50.0)
version: 1.10.12(eslint@8.50.0)
eslint-plugin-react:
specifier: 7.33.2
version: 7.33.2(eslint@8.50.0)
@@ -3386,27 +3389,6 @@ packages:
dev: false
optional: true
/@esbuild-kit/cjs-loader@2.4.2:
resolution: {integrity: sha512-BDXFbYOJzT/NBEtp71cvsrGPwGAMGRB/349rwKuoxNSiKjPraNNnlK6MIIabViCjqZugu6j+xeMDlEkWdHHJSg==}
dependencies:
'@esbuild-kit/core-utils': 3.0.0
get-tsconfig: 4.4.0
dev: true
/@esbuild-kit/core-utils@3.0.0:
resolution: {integrity: sha512-TXmwH9EFS3DC2sI2YJWJBgHGhlteK0Xyu1VabwetMULfm3oYhbrsWV5yaSr2NTWZIgDGVLHbRf0inxbjXqAcmQ==}
dependencies:
esbuild: 0.15.16
source-map-support: 0.5.21
dev: true
/@esbuild-kit/esm-loader@2.5.5:
resolution: {integrity: sha512-Qwfvj/qoPbClxCRNuac1Du01r9gvNOT+pMYtJDapfB1eoGN1YlJ1BixLyL9WVENRx5RXgNLdfYdx/CuswlGhMw==}
dependencies:
'@esbuild-kit/core-utils': 3.0.0
get-tsconfig: 4.4.0
dev: true
/@esbuild/android-arm64@0.16.4:
resolution: {integrity: sha512-VPuTzXFm/m2fcGfN6CiwZTlLzxrKsWbPkG7ArRFpuxyaHUm/XFHQPD4xNwZT6uUmpIHhnSjcaCmcla8COzmZ5Q==}
engines: {node: '>=12'}
@@ -3425,10 +3407,10 @@ packages:
dev: true
optional: true
/@esbuild/android-arm@0.15.16:
resolution: {integrity: sha512-nyB6CH++2mSgx3GbnrJsZSxzne5K0HMyNIWafDHqYy7IwxFc4fd/CgHVZXr8Eh+Q3KbIAcAe3vGyqIPhGblvMQ==}
/@esbuild/android-arm64@0.18.20:
resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==}
engines: {node: '>=12'}
cpu: [arm]
cpu: [arm64]
os: [android]
requiresBuild: true
dev: true
@@ -3452,6 +3434,15 @@ packages:
dev: true
optional: true
/@esbuild/android-arm@0.18.20:
resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==}
engines: {node: '>=12'}
cpu: [arm]
os: [android]
requiresBuild: true
dev: true
optional: true
/@esbuild/android-x64@0.16.4:
resolution: {integrity: sha512-MW+B2O++BkcOfMWmuHXB15/l1i7wXhJFqbJhp82IBOais8RBEQv2vQz/jHrDEHaY2X0QY7Wfw86SBL2PbVOr0g==}
engines: {node: '>=12'}
@@ -3470,6 +3461,15 @@ packages:
dev: true
optional: true
/@esbuild/android-x64@0.18.20:
resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==}
engines: {node: '>=12'}
cpu: [x64]
os: [android]
requiresBuild: true
dev: true
optional: true
/@esbuild/darwin-arm64@0.16.4:
resolution: {integrity: sha512-a28X1O//aOfxwJVZVs7ZfM8Tyih2Za4nKJrBwW5Wm4yKsnwBy9aiS/xwpxiiTRttw3EaTg4Srerhcm6z0bu9Wg==}
engines: {node: '>=12'}
@@ -3488,6 +3488,15 @@ packages:
dev: true
optional: true
/@esbuild/darwin-arm64@0.18.20:
resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==}
engines: {node: '>=12'}
cpu: [arm64]
os: [darwin]
requiresBuild: true
dev: true
optional: true
/@esbuild/darwin-x64@0.16.4:
resolution: {integrity: sha512-e3doCr6Ecfwd7VzlaQqEPrnbvvPjE9uoTpxG5pyLzr2rI2NMjDHmvY1E5EO81O/e9TUOLLkXA5m6T8lfjK9yAA==}
engines: {node: '>=12'}
@@ -3506,6 +3515,15 @@ packages:
dev: true
optional: true
/@esbuild/darwin-x64@0.18.20:
resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [darwin]
requiresBuild: true
dev: true
optional: true
/@esbuild/freebsd-arm64@0.16.4:
resolution: {integrity: sha512-Oup3G/QxBgvvqnXWrBed7xxkFNwAwJVHZcklWyQt7YCAL5bfUkaa6FVWnR78rNQiM8MqqLiT6ZTZSdUFuVIg1w==}
engines: {node: '>=12'}
@@ -3524,6 +3542,15 @@ packages:
dev: true
optional: true
/@esbuild/freebsd-arm64@0.18.20:
resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==}
engines: {node: '>=12'}
cpu: [arm64]
os: [freebsd]
requiresBuild: true
dev: true
optional: true
/@esbuild/freebsd-x64@0.16.4:
resolution: {integrity: sha512-vAP+eYOxlN/Bpo/TZmzEQapNS8W1njECrqkTpNgvXskkkJC2AwOXwZWai/Kc2vEFZUXQttx6UJbj9grqjD/+9Q==}
engines: {node: '>=12'}
@@ -3542,6 +3569,15 @@ packages:
dev: true
optional: true
/@esbuild/freebsd-x64@0.18.20:
resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [freebsd]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-arm64@0.16.4:
resolution: {integrity: sha512-2zXoBhv4r5pZiyjBKrOdFP4CXOChxXiYD50LRUU+65DkdS5niPFHbboKZd/c81l0ezpw7AQnHeoCy5hFrzzs4g==}
engines: {node: '>=12'}
@@ -3560,6 +3596,15 @@ packages:
dev: true
optional: true
/@esbuild/linux-arm64@0.18.20:
resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==}
engines: {node: '>=12'}
cpu: [arm64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-arm@0.16.4:
resolution: {integrity: sha512-A47ZmtpIPyERxkSvIv+zLd6kNIOtJH03XA0Hy7jaceRDdQaQVGSDt4mZqpWqJYgDk9rg96aglbF6kCRvPGDSUA==}
engines: {node: '>=12'}
@@ -3578,6 +3623,15 @@ packages:
dev: true
optional: true
/@esbuild/linux-arm@0.18.20:
resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==}
engines: {node: '>=12'}
cpu: [arm]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-ia32@0.16.4:
resolution: {integrity: sha512-uxdSrpe9wFhz4yBwt2kl2TxS/NWEINYBUFIxQtaEVtglm1eECvsj1vEKI0KX2k2wCe17zDdQ3v+jVxfwVfvvjw==}
engines: {node: '>=12'}
@@ -3596,10 +3650,10 @@ packages:
dev: true
optional: true
/@esbuild/linux-loong64@0.15.16:
resolution: {integrity: sha512-SDLfP1uoB0HZ14CdVYgagllgrG7Mdxhkt4jDJOKl/MldKrkQ6vDJMZKl2+5XsEY/Lzz37fjgLQoJBGuAw/x8kQ==}
/@esbuild/linux-ia32@0.18.20:
resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==}
engines: {node: '>=12'}
cpu: [loong64]
cpu: [ia32]
os: [linux]
requiresBuild: true
dev: true
@@ -3623,6 +3677,15 @@ packages:
dev: true
optional: true
/@esbuild/linux-loong64@0.18.20:
resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==}
engines: {node: '>=12'}
cpu: [loong64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-mips64el@0.16.4:
resolution: {integrity: sha512-sD9EEUoGtVhFjjsauWjflZklTNr57KdQ6xfloO4yH1u7vNQlOfAlhEzbyBKfgbJlW7rwXYBdl5/NcZ+Mg2XhQA==}
engines: {node: '>=12'}
@@ -3641,6 +3704,15 @@ packages:
dev: true
optional: true
/@esbuild/linux-mips64el@0.18.20:
resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==}
engines: {node: '>=12'}
cpu: [mips64el]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-ppc64@0.16.4:
resolution: {integrity: sha512-X1HSqHUX9D+d0l6/nIh4ZZJ94eQky8d8z6yxAptpZE3FxCWYWvTDd9X9ST84MGZEJx04VYUD/AGgciddwO0b8g==}
engines: {node: '>=12'}
@@ -3659,6 +3731,15 @@ packages:
dev: true
optional: true
/@esbuild/linux-ppc64@0.18.20:
resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==}
engines: {node: '>=12'}
cpu: [ppc64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-riscv64@0.16.4:
resolution: {integrity: sha512-97ANpzyNp0GTXCt6SRdIx1ngwncpkV/z453ZuxbnBROCJ5p/55UjhbaG23UdHj88fGWLKPFtMoU4CBacz4j9FA==}
engines: {node: '>=12'}
@@ -3677,6 +3758,15 @@ packages:
dev: true
optional: true
/@esbuild/linux-riscv64@0.18.20:
resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==}
engines: {node: '>=12'}
cpu: [riscv64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-s390x@0.16.4:
resolution: {integrity: sha512-pUvPQLPmbEeJRPjP0DYTC1vjHyhrnCklQmCGYbipkep+oyfTn7GTBJXoPodR7ZS5upmEyc8lzAkn2o29wD786A==}
engines: {node: '>=12'}
@@ -3695,6 +3785,15 @@ packages:
dev: true
optional: true
/@esbuild/linux-s390x@0.18.20:
resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==}
engines: {node: '>=12'}
cpu: [s390x]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-x64@0.16.4:
resolution: {integrity: sha512-N55Q0mJs3Sl8+utPRPBrL6NLYZKBCLLx0bme/+RbjvMforTGGzFvsRl4xLTZMUBFC1poDzBEPTEu5nxizQ9Nlw==}
engines: {node: '>=12'}
@@ -3713,6 +3812,15 @@ packages:
dev: true
optional: true
/@esbuild/linux-x64@0.18.20:
resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==}
engines: {node: '>=12'}
cpu: [x64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/netbsd-x64@0.16.4:
resolution: {integrity: sha512-LHSJLit8jCObEQNYkgsDYBh2JrJT53oJO2HVdkSYLa6+zuLJh0lAr06brXIkljrlI+N7NNW1IAXGn/6IZPi3YQ==}
engines: {node: '>=12'}
@@ -3731,6 +3839,15 @@ packages:
dev: true
optional: true
/@esbuild/netbsd-x64@0.18.20:
resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==}
engines: {node: '>=12'}
cpu: [x64]
os: [netbsd]
requiresBuild: true
dev: true
optional: true
/@esbuild/openbsd-x64@0.16.4:
resolution: {integrity: sha512-nLgdc6tWEhcCFg/WVFaUxHcPK3AP/bh+KEwKtl69Ay5IBqUwKDaq/6Xk0E+fh/FGjnLwqFSsarsbPHeKM8t8Sw==}
engines: {node: '>=12'}
@@ -3749,6 +3866,15 @@ packages:
dev: true
optional: true
/@esbuild/openbsd-x64@0.18.20:
resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==}
engines: {node: '>=12'}
cpu: [x64]
os: [openbsd]
requiresBuild: true
dev: true
optional: true
/@esbuild/sunos-x64@0.16.4:
resolution: {integrity: sha512-08SluG24GjPO3tXKk95/85n9kpyZtXCVwURR2i4myhrOfi3jspClV0xQQ0W0PYWHioJj+LejFMt41q+PG3mlAQ==}
engines: {node: '>=12'}
@@ -3767,6 +3893,15 @@ packages:
dev: true
optional: true
/@esbuild/sunos-x64@0.18.20:
resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [sunos]
requiresBuild: true
dev: true
optional: true
/@esbuild/win32-arm64@0.16.4:
resolution: {integrity: sha512-yYiRDQcqLYQSvNQcBKN7XogbrSvBE45FEQdH8fuXPl7cngzkCvpsG2H9Uey39IjQ6gqqc+Q4VXYHsQcKW0OMjQ==}
engines: {node: '>=12'}
@@ -3785,6 +3920,15 @@ packages:
dev: true
optional: true
/@esbuild/win32-arm64@0.18.20:
resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==}
engines: {node: '>=12'}
cpu: [arm64]
os: [win32]
requiresBuild: true
dev: true
optional: true
/@esbuild/win32-ia32@0.16.4:
resolution: {integrity: sha512-5rabnGIqexekYkh9zXG5waotq8mrdlRoBqAktjx2W3kb0zsI83mdCwrcAeKYirnUaTGztR5TxXcXmQrEzny83w==}
engines: {node: '>=12'}
@@ -3803,6 +3947,15 @@ packages:
dev: true
optional: true
/@esbuild/win32-ia32@0.18.20:
resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==}
engines: {node: '>=12'}
cpu: [ia32]
os: [win32]
requiresBuild: true
dev: true
optional: true
/@esbuild/win32-x64@0.16.4:
resolution: {integrity: sha512-sN/I8FMPtmtT2Yw+Dly8Ur5vQ5a/RmC8hW7jO9PtPSQUPkowxWpcUZnqOggU7VwyT3Xkj6vcXWd3V/qTXwultQ==}
engines: {node: '>=12'}
@@ -3821,6 +3974,15 @@ packages:
dev: true
optional: true
/@esbuild/win32-x64@0.18.20:
resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [win32]
requiresBuild: true
dev: true
optional: true
/@eslint-community/eslint-utils@4.4.0(eslint@8.50.0):
resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@@ -12146,216 +12308,6 @@ packages:
es6-symbol: 3.1.3
dev: true
/esbuild-android-64@0.15.16:
resolution: {integrity: sha512-Vwkv/sT0zMSgPSVO3Jlt1pUbnZuOgtOQJkJkyyJFAlLe7BiT8e9ESzo0zQSx4c3wW4T6kGChmKDPMbWTgtliQA==}
engines: {node: '>=12'}
cpu: [x64]
os: [android]
requiresBuild: true
dev: true
optional: true
/esbuild-android-arm64@0.15.16:
resolution: {integrity: sha512-lqfKuofMExL5niNV3gnhMUYacSXfsvzTa/58sDlBET/hCOG99Zmeh+lz6kvdgvGOsImeo6J9SW21rFCogNPLxg==}
engines: {node: '>=12'}
cpu: [arm64]
os: [android]
requiresBuild: true
dev: true
optional: true
/esbuild-darwin-64@0.15.16:
resolution: {integrity: sha512-wo2VWk/n/9V2TmqUZ/KpzRjCEcr00n7yahEdmtzlrfQ3lfMCf3Wa+0sqHAbjk3C6CKkR3WKK/whkMq5Gj4Da9g==}
engines: {node: '>=12'}
cpu: [x64]
os: [darwin]
requiresBuild: true
dev: true
optional: true
/esbuild-darwin-arm64@0.15.16:
resolution: {integrity: sha512-fMXaUr5ou0M4WnewBKsspMtX++C1yIa3nJ5R2LSbLCfJT3uFdcRoU/NZjoM4kOMKyOD9Sa/2vlgN8G07K3SJnw==}
engines: {node: '>=12'}
cpu: [arm64]
os: [darwin]
requiresBuild: true
dev: true
optional: true
/esbuild-freebsd-64@0.15.16:
resolution: {integrity: sha512-UzIc0xlRx5x9kRuMr+E3+hlSOxa/aRqfuMfiYBXu2jJ8Mzej4lGL7+o6F5hzhLqWfWm1GWHNakIdlqg1ayaTNQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [freebsd]
requiresBuild: true
dev: true
optional: true
/esbuild-freebsd-arm64@0.15.16:
resolution: {integrity: sha512-8xyiYuGc0DLZphFQIiYaLHlfoP+hAN9RHbE+Ibh8EUcDNHAqbQgUrQg7pE7Bo00rXmQ5Ap6KFgcR0b4ALZls1g==}
engines: {node: '>=12'}
cpu: [arm64]
os: [freebsd]
requiresBuild: true
dev: true
optional: true
/esbuild-linux-32@0.15.16:
resolution: {integrity: sha512-iGijUTV+0kIMyUVoynK0v+32Oi8yyp0xwMzX69GX+5+AniNy/C/AL1MjFTsozRp/3xQPl7jVux/PLe2ds10/2w==}
engines: {node: '>=12'}
cpu: [ia32]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-linux-64@0.15.16:
resolution: {integrity: sha512-tuSOjXdLw7VzaUj89fIdAaQT7zFGbKBcz4YxbWrOiXkwscYgE7HtTxUavreBbnRkGxKwr9iT/gmeJWNm4djy/g==}
engines: {node: '>=12'}
cpu: [x64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-linux-arm64@0.15.16:
resolution: {integrity: sha512-mPYksnfHnemNrvjrDhZyixL/AfbJN0Xn9S34ZOHYdh6/jJcNd8iTsv3JwJoEvTJqjMggjMhGUPJAdjnFBHoH8A==}
engines: {node: '>=12'}
cpu: [arm64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-linux-arm@0.15.16:
resolution: {integrity: sha512-XKcrxCEXDTOuoRj5l12tJnkvuxXBMKwEC5j0JISw3ziLf0j4zIwXbKbTmUrKFWbo6ZgvNpa7Y5dnbsjVvH39bQ==}
engines: {node: '>=12'}
cpu: [arm]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-linux-mips64le@0.15.16:
resolution: {integrity: sha512-kSJO2PXaxfm0pWY39+YX+QtpFqyyrcp0ZeI8QPTrcFVQoWEPiPVtOfTZeS3ZKedfH+Ga38c4DSzmKMQJocQv6A==}
engines: {node: '>=12'}
cpu: [mips64el]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-linux-ppc64le@0.15.16:
resolution: {integrity: sha512-NimPikwkBY0yGABw6SlhKrtT35sU4O23xkhlrTT/O6lSxv3Pm5iSc6OYaqVAHWkLdVf31bF4UDVFO+D990WpAA==}
engines: {node: '>=12'}
cpu: [ppc64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-linux-riscv64@0.15.16:
resolution: {integrity: sha512-ty2YUHZlwFOwp7pR+J87M4CVrXJIf5ZZtU/umpxgVJBXvWjhziSLEQxvl30SYfUPq0nzeWKBGw5i/DieiHeKfw==}
engines: {node: '>=12'}
cpu: [riscv64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-linux-s390x@0.15.16:
resolution: {integrity: sha512-VkZaGssvPDQtx4fvVdZ9czezmyWyzpQhEbSNsHZZN0BHvxRLOYAQ7sjay8nMQwYswP6O2KlZluRMNPYefFRs+w==}
engines: {node: '>=12'}
cpu: [s390x]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-netbsd-64@0.15.16:
resolution: {integrity: sha512-ElQ9rhdY51et6MJTWrCPbqOd/YuPowD7Cxx3ee8wlmXQQVW7UvQI6nSprJ9uVFQISqSF5e5EWpwWqXZsECLvXg==}
engines: {node: '>=12'}
cpu: [x64]
os: [netbsd]
requiresBuild: true
dev: true
optional: true
/esbuild-openbsd-64@0.15.16:
resolution: {integrity: sha512-KgxMHyxMCT+NdLQE1zVJEsLSt2QQBAvJfmUGDmgEq8Fvjrf6vSKB00dVHUEDKcJwMID6CdgCpvYNt999tIYhqA==}
engines: {node: '>=12'}
cpu: [x64]
os: [openbsd]
requiresBuild: true
dev: true
optional: true
/esbuild-sunos-64@0.15.16:
resolution: {integrity: sha512-exSAx8Phj7QylXHlMfIyEfNrmqnLxFqLxdQF6MBHPdHAjT7fsKaX6XIJn+aQEFiOcE4X8e7VvdMCJ+WDZxjSRQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [sunos]
requiresBuild: true
dev: true
optional: true
/esbuild-windows-32@0.15.16:
resolution: {integrity: sha512-zQgWpY5pUCSTOwqKQ6/vOCJfRssTvxFuEkpB4f2VUGPBpdddZfdj8hbZuFRdZRPIVHvN7juGcpgCA/XCF37mAQ==}
engines: {node: '>=12'}
cpu: [ia32]
os: [win32]
requiresBuild: true
dev: true
optional: true
/esbuild-windows-64@0.15.16:
resolution: {integrity: sha512-HjW1hHRLSncnM3MBCP7iquatHVJq9l0S2xxsHHj4yzf4nm9TU4Z7k4NkeMlD/dHQ4jPlQQhwcMvwbJiOefSuZw==}
engines: {node: '>=12'}
cpu: [x64]
os: [win32]
requiresBuild: true
dev: true
optional: true
/esbuild-windows-arm64@0.15.16:
resolution: {integrity: sha512-oCcUKrJaMn04Vxy9Ekd8x23O8LoU01+4NOkQ2iBToKgnGj5eo1vU9i27NQZ9qC8NFZgnQQZg5oZWAejmbsppNA==}
engines: {node: '>=12'}
cpu: [arm64]
os: [win32]
requiresBuild: true
dev: true
optional: true
/esbuild@0.15.16:
resolution: {integrity: sha512-o6iS9zxdHrrojjlj6pNGC2NAg86ECZqIETswTM5KmJitq+R1YmahhWtMumeQp9lHqJaROGnsBi2RLawGnfo5ZQ==}
engines: {node: '>=12'}
hasBin: true
requiresBuild: true
optionalDependencies:
'@esbuild/android-arm': 0.15.16
'@esbuild/linux-loong64': 0.15.16
esbuild-android-64: 0.15.16
esbuild-android-arm64: 0.15.16
esbuild-darwin-64: 0.15.16
esbuild-darwin-arm64: 0.15.16
esbuild-freebsd-64: 0.15.16
esbuild-freebsd-arm64: 0.15.16
esbuild-linux-32: 0.15.16
esbuild-linux-64: 0.15.16
esbuild-linux-arm: 0.15.16
esbuild-linux-arm64: 0.15.16
esbuild-linux-mips64le: 0.15.16
esbuild-linux-ppc64le: 0.15.16
esbuild-linux-riscv64: 0.15.16
esbuild-linux-s390x: 0.15.16
esbuild-netbsd-64: 0.15.16
esbuild-openbsd-64: 0.15.16
esbuild-sunos-64: 0.15.16
esbuild-windows-32: 0.15.16
esbuild-windows-64: 0.15.16
esbuild-windows-arm64: 0.15.16
dev: true
/esbuild@0.16.4:
resolution: {integrity: sha512-qQrPMQpPTWf8jHugLWHoGqZjApyx3OEm76dlTXobHwh/EBbavbRdjXdYi/GWr43GyN0sfpap14GPkb05NH3ROA==}
engines: {node: '>=12'}
@@ -12416,6 +12368,36 @@ packages:
'@esbuild/win32-x64': 0.18.10
dev: true
/esbuild@0.18.20:
resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==}
engines: {node: '>=12'}
hasBin: true
requiresBuild: true
optionalDependencies:
'@esbuild/android-arm': 0.18.20
'@esbuild/android-arm64': 0.18.20
'@esbuild/android-x64': 0.18.20
'@esbuild/darwin-arm64': 0.18.20
'@esbuild/darwin-x64': 0.18.20
'@esbuild/freebsd-arm64': 0.18.20
'@esbuild/freebsd-x64': 0.18.20
'@esbuild/linux-arm': 0.18.20
'@esbuild/linux-arm64': 0.18.20
'@esbuild/linux-ia32': 0.18.20
'@esbuild/linux-loong64': 0.18.20
'@esbuild/linux-mips64el': 0.18.20
'@esbuild/linux-ppc64': 0.18.20
'@esbuild/linux-riscv64': 0.18.20
'@esbuild/linux-s390x': 0.18.20
'@esbuild/linux-x64': 0.18.20
'@esbuild/netbsd-x64': 0.18.20
'@esbuild/openbsd-x64': 0.18.20
'@esbuild/sunos-x64': 0.18.20
'@esbuild/win32-arm64': 0.18.20
'@esbuild/win32-ia32': 0.18.20
'@esbuild/win32-x64': 0.18.20
dev: true
/escalade@3.1.1:
resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==}
engines: {node: '>=6'}
@@ -12530,13 +12512,13 @@ packages:
resolution: {integrity: sha512-NB/L/1Y30qyJcG5xZxCJKW/+bqyj+llbcCwo9DEz8bESIP0SLTOQ8T1DWCCFc+wJ61AMEstj4511PSScqMMfCw==}
dev: true
/eslint-config-turbo@1.10.15(eslint@8.50.0):
resolution: {integrity: sha512-76mpx2x818JZE26euen14utYcFDxOahZ9NaWA+6Xa4pY2ezVKVschuOxS96EQz3o3ZRSmcgBOapw/gHbN+EKxQ==}
/eslint-config-turbo@1.10.12(eslint@8.50.0):
resolution: {integrity: sha512-z3jfh+D7UGYlzMWGh+Kqz++hf8LOE96q3o5R8X4HTjmxaBWlLAWG+0Ounr38h+JLR2TJno0hU9zfzoPNkR9BdA==}
peerDependencies:
eslint: '>6.6.0'
dependencies:
eslint: 8.50.0
eslint-plugin-turbo: 1.10.15(eslint@8.50.0)
eslint-plugin-turbo: 1.10.12(eslint@8.50.0)
dev: true
/eslint-import-resolver-node@0.3.9:
@@ -12742,8 +12724,8 @@ packages:
semver: 6.3.1
string.prototype.matchall: 4.0.8
/eslint-plugin-turbo@1.10.15(eslint@8.50.0):
resolution: {integrity: sha512-Tv4QSKV/U56qGcTqS/UgOvb9HcKFmWOQcVh3HEaj7of94lfaENgfrtK48E2CckQf7amhKs1i+imhCsNCKjkQyA==}
/eslint-plugin-turbo@1.10.12(eslint@8.50.0):
resolution: {integrity: sha512-uNbdj+ohZaYo4tFJ6dStRXu2FZigwulR1b3URPXe0Q8YaE7thuekKNP+54CHtZPH9Zey9dmDx5btAQl9mfzGOw==}
peerDependencies:
eslint: '>6.6.0'
dependencies:
@@ -13721,6 +13703,14 @@ packages:
requiresBuild: true
optional: true
/fsevents@2.3.3:
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
os: [darwin]
requiresBuild: true
dev: true
optional: true
/fstream@1.0.12:
resolution: {integrity: sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==}
engines: {node: '>=0.6'}
@@ -13847,6 +13837,12 @@ packages:
/get-tsconfig@4.4.0:
resolution: {integrity: sha512-0Gdjo/9+FzsYhXCEFueo2aY1z1tpXrxWZzP7k8ul9qt1U5o8rYJwTJYmaeHdrVosYIVYkOy2iwCJ9FdpocJhPQ==}
/get-tsconfig@4.7.2:
resolution: {integrity: sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==}
dependencies:
resolve-pkg-maps: 1.0.0
dev: true
/get-value@2.0.6:
resolution: {integrity: sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==}
engines: {node: '>=0.10.0'}
@@ -13942,16 +13938,16 @@ packages:
- supports-color
dev: true
/glob@10.2.6:
resolution: {integrity: sha512-U/rnDpXJGF414QQQZv5uVsabTVxMSwzS5CH0p3DRCIV6ownl4f7PzGnkGmvlum2wB+9RlJWJZ6ACU1INnBqiPA==}
/glob@10.3.10:
resolution: {integrity: sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==}
engines: {node: '>=16 || 14 >=14.17'}
hasBin: true
dependencies:
foreground-child: 3.1.1
jackspeak: 2.2.0
jackspeak: 2.3.6
minimatch: 9.0.1
minipass: 5.0.0
path-scurry: 1.7.0
path-scurry: 1.10.1
dev: true
/glob@7.1.6:
@@ -15643,8 +15639,8 @@ packages:
has-symbols: 1.0.3
reflect.getprototypeof: 1.0.4
/jackspeak@2.2.0:
resolution: {integrity: sha512-r5XBrqIJfwRIjRt/Xr5fv9Wh09qyhHfKnYddDlpM+ibRR20qrYActpCAgU6U+d53EOEjzkvxPMVHSlgR7leXrQ==}
/jackspeak@2.3.6:
resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==}
engines: {node: '>=14'}
dependencies:
'@isaacs/cliui': 8.0.2
@@ -19261,8 +19257,8 @@ packages:
path-root-regex: 0.1.2
dev: true
/path-scurry@1.7.0:
resolution: {integrity: sha512-UkZUeDjczjYRE495+9thsgcVgsaCPkaw80slmfVFgllxY+IO8ubTsOpFVjDPROBqJdHfVPUFRHPBV/WciOVfWg==}
/path-scurry@1.10.1:
resolution: {integrity: sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==}
engines: {node: '>=16 || 14 >=14.17'}
dependencies:
lru-cache: 9.1.1
@@ -19720,23 +19716,6 @@ packages:
postcss: 8.4.27
yaml: 2.3.1
/postcss-load-config@4.0.1(postcss@8.4.31):
resolution: {integrity: sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==}
engines: {node: '>= 14'}
peerDependencies:
postcss: '>=8.0.9'
ts-node: '>=9.0.0'
peerDependenciesMeta:
postcss:
optional: true
ts-node:
optional: true
dependencies:
lilconfig: 2.1.0
postcss: 8.4.31
yaml: 2.3.1
dev: true
/postcss-loader@4.3.0(postcss@8.4.27)(webpack@4.46.0):
resolution: {integrity: sha512-M/dSoIiNDOo8Rk0mUqoj4kpGq91gcxCfb9PoyZVdZ76/AuhxylHDYZblNE8o+EQ9AMSASeMFEKxZf5aU6wlx1Q==}
engines: {node: '>= 10.13.0'}
@@ -22018,6 +21997,10 @@ packages:
value-or-function: 3.0.0
dev: true
/resolve-pkg-maps@1.0.0:
resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==}
dev: true
/resolve-url@0.2.1:
resolution: {integrity: sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==}
deprecated: https://github.com/lydell/resolve-url#deprecated
@@ -22119,12 +22102,12 @@ packages:
dependencies:
glob: 7.2.3
/rimraf@5.0.1:
resolution: {integrity: sha512-OfFZdwtd3lZ+XZzYP/6gTACubwFcHdLRqS9UX3UwpU2dnGQYkPFISRwvM3w9IiB2w7bW5qGo/uAwE4SmXXSKvg==}
/rimraf@5.0.5:
resolution: {integrity: sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A==}
engines: {node: '>=14'}
hasBin: true
dependencies:
glob: 10.2.6
glob: 10.3.10
dev: true
/ripemd160@2.0.2:
@@ -24177,7 +24160,7 @@ packages:
execa: 5.1.1
globby: 11.1.0
joycon: 3.1.1
postcss-load-config: 4.0.1(postcss@8.4.31)
postcss-load-config: 4.0.1(postcss@8.4.27)
resolve-from: 5.0.0
rollup: 3.29.4
source-map: 0.8.0-beta.0
@@ -24199,15 +24182,15 @@ packages:
typescript: 5.2.2
dev: true
/tsx@3.12.7:
resolution: {integrity: sha512-C2Ip+jPmqKd1GWVQDvz/Eyc6QJbGfE7NrR3fx5BpEHMZsEHoIxHL1j+lKdGobr8ovEyqeNkPLSKp6SCSOt7gmw==}
/tsx@3.13.0:
resolution: {integrity: sha512-rjmRpTu3as/5fjNq/kOkOtihgLxuIz6pbKdj9xwP4J5jOLkBxw/rjN5ANw+KyrrOXV5uB7HC8+SrrSJxT65y+A==}
hasBin: true
dependencies:
'@esbuild-kit/cjs-loader': 2.4.2
'@esbuild-kit/core-utils': 3.0.0
'@esbuild-kit/esm-loader': 2.5.5
esbuild: 0.18.20
get-tsconfig: 4.7.2
source-map-support: 0.5.21
optionalDependencies:
fsevents: 2.3.2
fsevents: 2.3.3
dev: true
/tty-browserify@0.0.0:
@@ -24233,64 +24216,65 @@ packages:
dependencies:
safe-buffer: 5.2.1
/turbo-darwin-64@1.10.15:
resolution: {integrity: sha512-Sik5uogjkRTe1XVP9TC2GryEMOJCaKE2pM/O9uLn4koQDnWKGcLQv+mDU+H+9DXvKLnJnKCD18OVRkwK5tdpoA==}
/turbo-darwin-64@1.10.12:
resolution: {integrity: sha512-vmDfGVPl5/aFenAbOj3eOx3ePNcWVUyZwYr7taRl0ZBbmv2TzjRiFotO4vrKCiTVnbqjQqAFQWY2ugbqCI1kOQ==}
cpu: [x64]
os: [darwin]
requiresBuild: true
dev: true
optional: true
/turbo-darwin-arm64@1.10.15:
resolution: {integrity: sha512-xwqyFDYUcl2xwXyGPmHkmgnNm4Cy0oNzMpMOBGRr5x64SErS7QQLR4VHb0ubiR+VAb8M+ECPklU6vD1Gm+wekg==}
/turbo-darwin-arm64@1.10.12:
resolution: {integrity: sha512-3JliEESLNX2s7g54SOBqqkqJ7UhcOGkS0ywMr5SNuvF6kWVTbuUq7uBU/sVbGq8RwvK1ONlhPvJne5MUqBCTCQ==}
cpu: [arm64]
os: [darwin]
requiresBuild: true
dev: true
optional: true
/turbo-linux-64@1.10.15:
resolution: {integrity: sha512-dM07SiO3RMAJ09Z+uB2LNUSkPp3I1IMF8goH5eLj+d8Kkwoxd/+qbUZOj9RvInyxU/IhlnO9w3PGd3Hp14m/nA==}
/turbo-linux-64@1.10.12:
resolution: {integrity: sha512-siYhgeX0DidIfHSgCR95b8xPee9enKSOjCzx7EjTLmPqPaCiVebRYvbOIYdQWRqiaKh9yfhUtFmtMOMScUf1gg==}
cpu: [x64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/turbo-linux-arm64@1.10.15:
resolution: {integrity: sha512-MkzKLkKYKyrz4lwfjNXH8aTny5+Hmiu4SFBZbx+5C0vOlyp6fV5jZANDBvLXWiDDL4DSEAuCEK/2cmN6FVH1ow==}
/turbo-linux-arm64@1.10.12:
resolution: {integrity: sha512-K/ZhvD9l4SslclaMkTiIrnfcACgos79YcAo4kwc8bnMQaKuUeRpM15sxLpZp3xDjDg8EY93vsKyjaOhdFG2UbA==}
cpu: [arm64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/turbo-windows-64@1.10.15:
resolution: {integrity: sha512-3TdVU+WEH9ThvQGwV3ieX/XHebtYNHv9HARHauPwmVj3kakoALkpGxLclkHFBLdLKkqDvmHmXtcsfs6cXXRHJg==}
/turbo-windows-64@1.10.12:
resolution: {integrity: sha512-7FSgSwvktWDNOqV65l9AbZwcoueAILeE4L7JvjauNASAjjbuzXGCEq5uN8AQU3U5BOFj4TdXrVmO2dX+lLu8Zg==}
cpu: [x64]
os: [win32]
requiresBuild: true
dev: true
optional: true
/turbo-windows-arm64@1.10.15:
resolution: {integrity: sha512-l+7UOBCbfadvPMYsX08hyLD+UIoAkg6ojfH+E8aud3gcA1padpjCJTh9gMpm3QdMbKwZteT5uUM+wyi6Rbbyww==}
/turbo-windows-arm64@1.10.12:
resolution: {integrity: sha512-gCNXF52dwom1HLY9ry/cneBPOKTBHhzpqhMylcyvJP0vp9zeMQQkt6yjYv+6QdnmELC92CtKNp2FsNZo+z0pyw==}
cpu: [arm64]
os: [win32]
requiresBuild: true
dev: true
optional: true
/turbo@1.10.15:
resolution: {integrity: sha512-mKKkqsuDAQy1wCCIjCdG+jOCwUflhckDMSRoeBPcIL/CnCl7c5yRDFe7SyaXloUUkt4tUR0rvNIhVCcT7YeQpg==}
/turbo@1.10.12:
resolution: {integrity: sha512-WM3+jTfQWnB9W208pmP4oeehZcC6JQNlydb/ZHMRrhmQa+htGhWLCzd6Q9rLe0MwZLPpSPFV2/bN5egCLyoKjQ==}
hasBin: true
requiresBuild: true
optionalDependencies:
turbo-darwin-64: 1.10.15
turbo-darwin-arm64: 1.10.15
turbo-linux-64: 1.10.15
turbo-linux-arm64: 1.10.15
turbo-windows-64: 1.10.15
turbo-windows-arm64: 1.10.15
turbo-darwin-64: 1.10.12
turbo-darwin-arm64: 1.10.12
turbo-linux-64: 1.10.12
turbo-linux-arm64: 1.10.12
turbo-windows-64: 1.10.12
turbo-windows-arm64: 1.10.12
dev: true
/tw-to-css@0.0.11: