feat: generalisedropdown menu item and content

This commit is contained in:
devcodes9
2024-10-10 23:58:46 +05:30
parent 666f699858
commit 8adf9be9d3
15 changed files with 98 additions and 106 deletions
@@ -141,37 +141,33 @@ export function ConditionalLogic({
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem
className="flex items-center gap-2"
onClick={() => {
duplicateLogic(logicItemIdx);
}}>
<CopyIcon className="h-4 w-4" />
}}
icon={<CopyIcon className="h-4 w-4" />}>
Duplicate
</DropdownMenuItem>
<DropdownMenuItem
className="flex items-center gap-2"
disabled={logicItemIdx === 0}
onClick={() => {
moveLogic(logicItemIdx, logicItemIdx - 1);
}}>
<ArrowUpIcon className="h-4 w-4" />
}}
icon={<ArrowUpIcon className="h-4 w-4" />}>
Move up
</DropdownMenuItem>
<DropdownMenuItem
className="flex items-center gap-2"
disabled={logicItemIdx === (question.logic ?? []).length - 1}
onClick={() => {
moveLogic(logicItemIdx, logicItemIdx + 1);
}}>
<ArrowDownIcon className="h-4 w-4" />
}}
icon={<ArrowDownIcon className="h-4 w-4" />}>
Move down
</DropdownMenuItem>
<DropdownMenuItem
className="flex items-center gap-2"
onClick={() => {
handleRemoveLogic(logicItemIdx);
}}>
<TrashIcon className="h-4 w-4" />
}}
icon={<TrashIcon className="h-4 w-4" />}>
Remove
</DropdownMenuItem>
</DropdownMenuContent>
@@ -162,7 +162,7 @@ export const EditorCardMenu = ({
<EllipsisIcon className="h-4 w-4 text-slate-500 hover:text-slate-600" />
</DropdownMenuTrigger>
<DropdownMenuContent className="border border-slate-200">
<DropdownMenuContent>
<div className="flex flex-col">
{cardType === "question" && (
<DropdownMenuSub>
@@ -170,13 +170,12 @@ export const EditorCardMenu = ({
Change question type
</DropdownMenuSubTrigger>
<DropdownMenuSubContent className="ml-2 border border-slate-200 text-slate-600 hover:text-slate-700">
<DropdownMenuSubContent className="ml-2">
{Object.entries(availableQuestionTypes).map(([type, name]) => {
if (type === card.type) return null;
return (
<DropdownMenuItem
key={type}
className="min-h-8 cursor-pointer"
onClick={() => {
setChangeToType(type as TSurveyQuestionTypeEnum);
if ((card as TSurveyQuestion).logic) {
@@ -185,8 +184,8 @@ export const EditorCardMenu = ({
}
changeQuestionType(type as TSurveyQuestionTypeEnum);
}}>
{QUESTIONS_ICON_MAP[type as TSurveyQuestionTypeEnum]}
}}
icon={QUESTIONS_ICON_MAP[type as TSurveyQuestionTypeEnum]}>
<span className="ml-2">{name}</span>
</DropdownMenuItem>
);
@@ -196,7 +195,7 @@ export const EditorCardMenu = ({
)}
{cardType === "ending" && (
<DropdownMenuItem
className="flex min-h-8 cursor-pointer justify-between text-slate-600 hover:text-slate-700"
className="min-h-8 justify-between"
onClick={(e) => {
e.preventDefault();
addEndingCardBelow();
@@ -211,12 +210,12 @@ export const EditorCardMenu = ({
Add question below
</DropdownMenuSubTrigger>
<DropdownMenuSubContent className="ml-4 border border-slate-200">
<DropdownMenuSubContent className="ml-2">
{Object.entries(availableQuestionTypes).map(([type, name]) => {
return (
<DropdownMenuItem
key={type}
className="min-h-8 cursor-pointer"
className="min-h-8"
onClick={(e) => {
e.stopPropagation();
if (cardType === "question") {
@@ -232,33 +231,27 @@ export const EditorCardMenu = ({
</DropdownMenuSub>
)}
<DropdownMenuItem
className={`flex min-h-8 cursor-pointer justify-between text-slate-600 hover:text-slate-700 ${
cardIdx === 0 ? "opacity-50" : ""
}`}
onClick={(e) => {
if (cardIdx !== 0) {
e.stopPropagation();
moveCard(cardIdx, true);
}
}}
icon={<ArrowUpIcon className="h-4 w-4" />}
disabled={cardIdx === 0}>
<span className="text-sm">Move up</span>
<ArrowUpIcon className="h-4 w-4" />
<span>Move up</span>
</DropdownMenuItem>
<DropdownMenuItem
className={`flex min-h-8 cursor-pointer justify-between text-slate-600 hover:text-slate-700 ${
lastCard ? "opacity-50" : ""
}`}
onClick={(e) => {
if (!lastCard) {
e.stopPropagation();
moveCard(cardIdx, false);
}
}}
icon={<ArrowDownIcon className="h-4 w-4" />}
disabled={lastCard}>
<span className="text-sm text-slate-600 hover:text-slate-700">Move down</span>
<ArrowDownIcon className="h-4 w-4" />
<span>Move down</span>
</DropdownMenuItem>
</div>
</DropdownMenuContent>
@@ -200,30 +200,27 @@ export function LogicEditorActions({
<DropdownMenuContent>
<DropdownMenuItem
className="flex items-center gap-2"
onClick={() => {
handleActionsChange("addBelow", idx);
}}>
<PlusIcon className="h-4 w-4" />
}}
icon={<PlusIcon className="h-4 w-4" />}>
Add action below
</DropdownMenuItem>
<DropdownMenuItem
className="flex items-center gap-2"
disabled={actions.length === 1}
onClick={() => {
handleActionsChange("remove", idx);
}}>
<TrashIcon className="h-4 w-4" />
}}
icon={<TrashIcon className="h-4 w-4" />}>
Remove
</DropdownMenuItem>
<DropdownMenuItem
className="flex items-center gap-2"
onClick={() => {
handleActionsChange("duplicate", idx);
}}>
<CopyIcon className="h-4 w-4" />
}}
icon={<CopyIcon className="h-4 w-4" />}>
Duplicate
</DropdownMenuItem>
</DropdownMenuContent>
@@ -216,18 +216,16 @@ export function LogicEditorConditions({
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem
className="flex items-center gap-2"
onClick={() => {
handleAddConditionBelow(condition.id);
}}>
<PlusIcon className="h-4 w-4" />
}}
icon={<PlusIcon className="h-4 w-4" />}>
Add condition below
</DropdownMenuItem>
<DropdownMenuItem
className="flex items-center gap-2"
disabled={depth === 0 && conditions.conditions.length === 1}
onClick={() => handleRemoveCondition(condition.id)}>
<TrashIcon className="h-4 w-4" />
onClick={() => handleRemoveCondition(condition.id)}
icon={<TrashIcon className="h-4 w-4" />}>
Remove
</DropdownMenuItem>
</DropdownMenuContent>
@@ -307,30 +305,26 @@ export function LogicEditorConditions({
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem
className="flex items-center gap-2"
onClick={() => {
handleAddConditionBelow(condition.id);
}}>
<PlusIcon className="h-4 w-4" />
}}
icon={<PlusIcon className="h-4 w-4" />}>
Add condition below
</DropdownMenuItem>
<DropdownMenuItem
className="flex items-center gap-2"
disabled={depth === 0 && conditions.conditions.length === 1}
onClick={() => handleRemoveCondition(condition.id)}>
<TrashIcon className="h-4 w-4" />
onClick={() => handleRemoveCondition(condition.id)}
icon={<TrashIcon className="h-4 w-4" />}>
Remove
</DropdownMenuItem>
<DropdownMenuItem
className="flex items-center gap-2"
onClick={() => handleDuplicateCondition(condition.id)}>
<CopyIcon className="h-4 w-4" />
onClick={() => handleDuplicateCondition(condition.id)}
icon={<CopyIcon className="h-4 w-4" />}>
Duplicate
</DropdownMenuItem>
<DropdownMenuItem
className="flex items-center gap-2"
onClick={() => handleCreateGroup(condition.id)}>
<WorkflowIcon className="h-4 w-4" />
onClick={() => handleCreateGroup(condition.id)}
icon={<WorkflowIcon className="h-4 w-4" />}>
Create group
</DropdownMenuItem>
</DropdownMenuContent>
@@ -323,7 +323,10 @@ export const MainNavigation = ({
)}
<DropdownMenu>
<DropdownMenuTrigger asChild id="productDropdownTrigger">
<DropdownMenuTrigger
asChild
id="productDropdownTrigger"
className="w-full rounded-br-xl border-t py-4 transition-colors duration-200 hover:bg-slate-50 focus:outline-none">
<div
tabIndex={0}
className={cn(
@@ -403,8 +406,9 @@ export const MainNavigation = ({
</DropdownMenuRadioGroup>
<DropdownMenuSeparator />
{isOwnerOrAdmin && (
<DropdownMenuItem onClick={() => handleAddProduct(organization.id)}>
<PlusIcon className="mr-2 h-4 w-4" />
<DropdownMenuItem
onClick={() => handleAddProduct(organization.id)}
icon={<PlusIcon className="mr-2 h-4 w-4" />}>
<span>Add product</span>
</DropdownMenuItem>
)}
@@ -463,8 +467,9 @@ export const MainNavigation = ({
target={link.target}
key={link.label}
className="flex items-center">
<DropdownMenuItem key={link.label}>
<link.icon className="h-4 w-4" strokeWidth={1.5} />
<DropdownMenuItem
key={link.label}
icon={<link.icon className="h-4 w-4" strokeWidth={1.5} />}>
{link.label}
</DropdownMenuItem>
</Link>
@@ -477,8 +482,8 @@ export const MainNavigation = ({
onClick={async () => {
await signOut({ callbackUrl: "/auth/login" });
await formbricksLogout();
}}>
<LogOutIcon className="h-4 w-4" strokeWidth={1.5} />
}}
icon={<LogOutIcon className="h-4 w-4" strokeWidth={1.5} />}>
Logout
</DropdownMenuItem>
@@ -510,8 +515,9 @@ export const MainNavigation = ({
</DropdownMenuRadioGroup>
<DropdownMenuSeparator />
{isMultiOrgEnabled && (
<DropdownMenuItem onClick={() => setShowCreateOrganizationModal(true)}>
<PlusIcon className="mr-2 h-4 w-4" />
<DropdownMenuItem
onClick={() => setShowCreateOrganizationModal(true)}
icon={<PlusIcon className="mr-2 h-4 w-4" />}>
<span>Create new organization</span>
</DropdownMenuItem>
)}
@@ -223,7 +223,6 @@ export const CustomFilter = ({ survey }: CustomFilterProps) => {
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem
className="hover:ring-0"
onClick={() => {
setFilterRange(FilterDropDownLabels.ALL_TIME);
setDateRange({ from: undefined, to: getTodayDate() });
@@ -231,7 +230,6 @@ export const CustomFilter = ({ survey }: CustomFilterProps) => {
<p className="text-slate-700">All time</p>
</DropdownMenuItem>
<DropdownMenuItem
className="hover:ring-0"
onClick={() => {
setFilterRange(FilterDropDownLabels.LAST_7_DAYS);
setDateRange({ from: startOfDay(subDays(new Date(), 7)), to: getTodayDate() });
@@ -239,7 +237,6 @@ export const CustomFilter = ({ survey }: CustomFilterProps) => {
<p className="text-slate-700">Last 7 days</p>
</DropdownMenuItem>
<DropdownMenuItem
className="hover:ring-0"
onClick={() => {
setFilterRange(FilterDropDownLabels.LAST_30_DAYS);
setDateRange({ from: startOfDay(subDays(new Date(), 30)), to: getTodayDate() });
@@ -247,7 +244,6 @@ export const CustomFilter = ({ survey }: CustomFilterProps) => {
<p className="text-slate-700">Last 30 days</p>
</DropdownMenuItem>
<DropdownMenuItem
className="hover:ring-0"
onClick={() => {
setIsDatePickerOpen(true);
setFilterRange(FilterDropDownLabels.CUSTOM_RANGE);
@@ -274,28 +270,24 @@ export const CustomFilter = ({ survey }: CustomFilterProps) => {
<DropdownMenuContent align="start">
<DropdownMenuItem
className="hover:ring-0"
onClick={() => {
handleDowndloadResponses(FilterDownload.ALL, "csv");
}}>
<p className="text-slate-700">All responses (CSV)</p>
</DropdownMenuItem>
<DropdownMenuItem
className="hover:ring-0"
onClick={() => {
handleDowndloadResponses(FilterDownload.ALL, "xlsx");
}}>
<p className="text-slate-700">All responses (Excel)</p>
</DropdownMenuItem>
<DropdownMenuItem
className="hover:ring-0"
onClick={() => {
handleDowndloadResponses(FilterDownload.FILTER, "csv");
}}>
<p className="text-slate-700">Current selection (CSV)</p>
</DropdownMenuItem>
<DropdownMenuItem
className="hover:ring-0"
onClick={() => {
handleDowndloadResponses(FilterDownload.FILTER, "xlsx");
}}>
@@ -98,34 +98,29 @@ export const ResultsShareButton = ({ survey, webAppUrl }: ResultsShareButtonProp
<DropdownMenuContent align="start">
{survey.resultShareKey ? (
<DropdownMenuItem
className="hover:ring-0"
onClick={() => {
navigator.clipboard.writeText(surveyUrl);
toast.success("Link to public results copied");
}}>
<p className="text-slate-700">
Copy link to public results <CopyIcon className="ml-1.5 inline h-4 w-4" />
</p>
}}
icon={<CopyIcon className="ml-1.5 inline h-4 w-4" />}>
<p className="text-slate-700">Copy link to public results</p>
</DropdownMenuItem>
) : (
<DropdownMenuItem
className="text-slate-700 hover:ring-0"
onClick={() => {
copyUrlToClipboard();
}}>
<p className="flex items-center text-slate-700">
Copy link <CopyIcon className="ml-1.5 h-4 w-4" />
</p>
}}
icon={<CopyIcon className="ml-1.5 h-4 w-4" />}>
<p className="flex items-center text-slate-700">Copy link</p>
</DropdownMenuItem>
)}
<DropdownMenuItem
className="hover:ring-0"
onClick={() => {
setShowResultsLinkModal(true);
}}>
}}
icon={<GlobeIcon className="ml-1.5 h-4 w-4" />}>
<p className="flex items-center text-slate-700">
{survey.resultShareKey ? "Unpublish from web" : "Publish to web"}
<GlobeIcon className="ml-1.5 h-4 w-4" />
</p>
</DropdownMenuItem>
</DropdownMenuContent>