Skip to content

Commit

Permalink
Fixes (#375)
Browse files Browse the repository at this point in the history
* fixed issue with multiselect z index

* better accessibility for multiselect

* template replacement fix inside text
  • Loading branch information
armintalaie authored Jan 10, 2025
1 parent 9207492 commit 01aa3d6
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 103 deletions.
1 change: 0 additions & 1 deletion src/app/portal/admin/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,6 @@ export async function getAllFormDetails(
formId: bigint,
): Promise<{ rawForm: any; formFields: FormFields; submissions: any[] }> {
try {
console.log("retrieving form details");
const form = await db.forms.findFirst({
where: { id: formId },
});
Expand Down
38 changes: 7 additions & 31 deletions src/components/forms/applications/data-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,30 +31,6 @@ import {
} from "@/components/forms/applications/columns";
import MultiSelect from "@/components/general/multiSelect";

// const getCommonPinningStyles = (column: Column<any>): CSSProperties => {
// const isPinned = column.getIsPinned();
// const isLastLeftPinnedColumn =
// isPinned === "left" && column.getIsLastColumn("left");
// const isFirstRightPinnedColumn =
// isPinned === "right" && column.getIsFirstColumn("right");

// return {
// ...(isLastLeftPinnedColumn
// ? { borderRight: `1px solid var(--background-500)` }
// : {}),
// ...(isFirstRightPinnedColumn
// ? { borderLeft: `1px solid var(--background-500)` }
// : {}),
// left: isPinned === "left" ? `${column.getStart("left")}px` : undefined,
// right: isPinned === "right" ? `${column.getAfter("right")}px` : undefined,
// opacity: isPinned ? 0.95 : 1,
// position: isPinned ? "sticky" : "relative",
// width: column.getSize() ? column.getSize() : "500px",
// zIndex: isPinned ? 1 : 0,
// maxWidth: column.getSize() ? column.getSize() : "500px",
// };
// };

const getCommonPinningStyles = (column: Column<Person>): CSSProperties => {
const isPinned = column.getIsPinned();
const isLastLeftPinnedColumn =
Expand All @@ -67,9 +43,9 @@ const getCommonPinningStyles = (column: Column<Person>): CSSProperties => {
left: isPinned === "left" ? `${column.getStart("left")}px` : undefined,
right: isPinned === "right" ? `${column.getAfter("right")}px` : undefined,
opacity: isPinned ? 0.95 : 1,
position: isPinned ? "sticky" : "relative",
// position: isPinned ? "sticky" : "relative",
width: column.getSize(),
zIndex: isPinned ? 1 : 0,
// zIndex: isPinned ? 1 : 0,
};
};

Expand Down Expand Up @@ -168,9 +144,9 @@ export function DataTable<TData, TValue>({
/>
)}
{tabView === "table" && (
<div className="rounded-md border shadow-sm overflow-auto min-h-0 border-background-500">
<Table className="w-full h-[1px] relative ">
<thead className={" left-0 top-0 z-20 sticky"}>
<div className="rounded-md border shadow-sm overflow-auto min-h-0 border-background-500 ">
<Table className="w-full h-[1px] ">
<thead className={" left-0 top-0 "}>
{table.getHeaderGroups().map((headerGroup) => (
<tr
key={headerGroup.id}
Expand All @@ -197,7 +173,7 @@ export function DataTable<TData, TValue>({
</tr>
))}
</thead>
<tbody className={"relative overflow-auto "}>
<tbody className={" overflow-auto "}>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => (
<tr
Expand Down Expand Up @@ -292,7 +268,7 @@ function TableFilter({
onClick={() => setShowFilters(false)}
></button>
<div
className=" min-h-screen gap-3 items-center justify-center static overflow-y-scroll w-screen flex flex-col pointer-events-none transform z-40 overflow-hidden p-2
className=" min-h-screen gap-3 items-center justify-center static overflow-y-scroll w-screen flex flex-col pointer-events-none transform overflow-hidden p-2
left-0 top-0
"
>
Expand Down
7 changes: 2 additions & 5 deletions src/components/forms/applications/offerPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export default function OfferPage({ form, app }: { form: Form; app: any }) {
const lpData = useContext(lpContext);
const cleanedText = replaceTemplateValues(text, {
general: lpData,
app: { ...rest, ...details },
app: { ...rest.applications, ...details },
});

async function handleDecision(decision: "accepted" | "declined") {
Expand Down Expand Up @@ -126,13 +126,10 @@ function replaceTemplateValues(
matches.forEach((match) => {
const key = match.replace("{{", "").replace("}}", "");
const multiKey = key.split(":");

if (multiKey.length > 1) {
const [firstKey, secondKey] = multiKey;
if (Object.keys(app).includes(secondKey)) {
if (app[secondKey] === undefined) {
newText = "";
} else {
if (app[secondKey] !== undefined) {
if (multiKey.length > 2) {
const thirdKey = multiKey[2];
newText = newText.replace(
Expand Down
205 changes: 139 additions & 66 deletions src/components/general/multiSelect.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,118 @@
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
//@ts-nocheck
"use client";

import { useEffect, useRef, useState } from "react";
import { cn } from "@/lib/utils/helpers";

function OptionsDropdown({
options,
value,
onChange,
allowMultiple,
onClose,
listboxId,
}: {
options: Record<string, string>[];
value: (string | number)[];
onChange: (value: string[]) => void;
allowMultiple: boolean;
onClose: () => void;
listboxId: string;
}) {
const [activeIndex, setActiveIndex] = useState(-1);
const listRef = useRef<HTMLUListElement>(null);

useEffect(() => {
if (activeIndex >= 0) {
const activeItem = listRef.current?.children[activeIndex] as HTMLElement;
activeItem?.focus();
}
}, [activeIndex]);

return (
<ul
id={listboxId}
ref={listRef}
role="listbox"
aria-label="Options"
aria-multiselectable={allowMultiple}
tabIndex={-1}
className="absolute border border-background-500 top-full mt-1 max-h-80 w-full overflow-y-scroll bg-background-700 flex flex-col rounded shadow-lg transform overflow-hidden z-50"
>
{options.map((option, index) => (
<li
key={index}
role="option"
tabIndex={0}
aria-selected={value?.includes(option.value)}
className={cn(
"flex items-center h-10 flex-shrink-0 gap-2 p-2 outline-none :not(:first) border-t border-background-500",
value?.includes(option.value)
? "bg-lp-500"
: "hover:bg-background-800 bg-opacity-45",
"focus:ring-2 focus:ring-lp-500",
)}
onClick={() => {
if (!allowMultiple) {
if (value?.includes(option.value)) {
onChange([]);
onClose();
return;
}
onChange([option.value]);
onClose();
return;
}
if (value?.includes(option.value)) {
onChange(value.filter((value) => value !== option.value));
} else {
onChange([...(value || []), option.value]);
}
}}
onKeyDown={(e) => {
switch (e.key) {
case "Enter":
case " ":
e.preventDefault();
if (!allowMultiple) {
if (value?.includes(option.value)) {
onChange([]);
onClose();
return;
}
onChange([option.value]);
onClose();
return;
}
if (value?.includes(option.value)) {
onChange(value.filter((value) => value !== option.value));
} else {
onChange([...(value || []), option.value]);
}
break;
case "ArrowDown":
e.preventDefault();
setActiveIndex(Math.min(index + 1, options.length - 1));
break;
case "ArrowUp":
e.preventDefault();
setActiveIndex(Math.max(index - 1, 0));
break;
case "Escape":
e.preventDefault();
onClose();
break;
}
}}
>
<span className="w-6">
{value?.includes(option.value) ? "✓" : ""}
</span>
<span>{option.label}</span>
</li>
))}
</ul>
);
}

export default function MultiSelect({
options,
value,
Expand All @@ -24,16 +132,12 @@ export default function MultiSelect({
}) {
const [isOpen, setIsOpen] = useState(false);
const selectedOptions = options.filter((option) => {
if (value === null) {
return false;
}
if (Array.isArray(value)) {
return value.includes(option.value);
}
console.log(value, option.value);
if (value === null) return false;
if (Array.isArray(value)) return value.includes(option.value);
return value === option.value;
});

const listboxId = "multiselect-listbox";
const ref = useRef<HTMLDivElement>(null);

useEffect(() => {
Expand All @@ -43,79 +147,48 @@ export default function MultiSelect({
}, [isOpen]);

return (
<div className="relative flex flex-col w-full" ref={ref}>
<div className="relative w-full" ref={ref}>
{isOpen && (
<div
onClick={() => setIsOpen(false)}
className="fixed inset-0 z-40 bg-black bg-opacity-50"
></div>
)}

<button
aria-haspopup="listbox"
aria-expanded={isOpen}
aria-controls={listboxId}
className={cn(
"flex border border-background-600 bg-background-700 z-10 items-center min-h-11 rounded p-2 gap-2",
"flex border border-background-600 bg-background-700 items-center min-h-11 rounded p-2 gap-2 w-full",
className,
isOpen && "border border-background-500 border-solid",
)}
onClick={() => setIsOpen(!isOpen)}
type="button"
>
<span className="text-white justify-center flex z-1 items-center gap-2 ">
{/*{JSON.stringify(selectedOptions)}*/}
<span className="text-white justify-center flex items-center gap-2">
{selectedOptions !== [null] &&
selectedOptions.map((option) => (
<span
key={option.value}
className="p-0.5 px-2 z-1! rounded bg-lp-500"
>
<span key={option.value} className="p-0.5 px-2 rounded bg-lp-500">
{option.label}
</span>
))}
<span className={"text-background-200 text-sm"}>
<span className="text-background-200 text-sm">
{selectedOptions.length === 0 && emptyText}
</span>
</span>
</button>

{isOpen && (
<div
className="fixed h-screen w-screen bg-black bg-opacity-30 z-20 top-0 left-0"
onClick={() => setIsOpen(false)}
></div>
)}
{isOpen && (
<div
className="fixed max-h-80 overflow-y-scroll bg-background-700 gap-1 flex flex-col rounded border border-background-600 shadow-lg w-full transform z-40 overflow-hidden p-2 "
style={{
top: ref.current?.getBoundingClientRect().top,
left: ref.current?.getBoundingClientRect().left,
width: ref.current?.getBoundingClientRect().width,
}}
>
{options.map((option, index) => (
<div
key={index}
className={`flex items-center h-10 z-40 flex-shrink-0 gap-2 rounded p-2 ${
value?.includes(option.value)
? "bg-lp-500"
: "hover:bg-background-800 bg-opacity-45"
}`}
onClick={() => {
if (!allowMultiple) {
if (value?.includes(option.value)) {
onChange([]);
setIsOpen(false);
return;
}
onChange([option.value]);
setIsOpen(false);
return;
}
if (value?.includes(option.value)) {
onChange(value.filter((value) => value !== option.value));
} else {
onChange([...(value || []), option.value]);
}
}}
>
<span className="w-6">
{value?.includes(option.value) ? "✓" : ""}
</span>
<span>{option.label}</span>
</div>
))}
</div>
<OptionsDropdown
options={options}
value={value}
onChange={onChange}
allowMultiple={allowMultiple}
onClose={() => setIsOpen(false)}
listboxId={listboxId}
/>
)}
</div>
);
Expand Down

0 comments on commit 01aa3d6

Please sign in to comment.