diff --git a/.changeset/strange-waves-rush.md b/.changeset/strange-waves-rush.md
new file mode 100644
index 00000000..c48a981d
--- /dev/null
+++ b/.changeset/strange-waves-rush.md
@@ -0,0 +1,5 @@
+---
+"@premieroctet/next-admin": patch
+---
+
+Add loader on select
diff --git a/apps/example/app/[locale]/admin/[[...nextadmin]]/page.tsx b/apps/example/app/[locale]/admin/[[...nextadmin]]/page.tsx
index 9313f40f..3987db28 100644
--- a/apps/example/app/[locale]/admin/[[...nextadmin]]/page.tsx
+++ b/apps/example/app/[locale]/admin/[[...nextadmin]]/page.tsx
@@ -17,7 +17,7 @@ export const viewport: Viewport = {
};
export const metadata: Metadata = {
- icons: "/favicon.ico",
+ icons: "/assets/logo.svg",
};
export default async function AdminPage({
diff --git a/apps/example/public/assets/logo.svg b/apps/example/public/assets/logo.svg
new file mode 100644
index 00000000..21a409c6
--- /dev/null
+++ b/apps/example/public/assets/logo.svg
@@ -0,0 +1,92 @@
+
\ No newline at end of file
diff --git a/packages/next-admin/src/components/Form.tsx b/packages/next-admin/src/components/Form.tsx
index 4f987519..2fb6c478 100644
--- a/packages/next-admin/src/components/Form.tsx
+++ b/packages/next-admin/src/components/Form.tsx
@@ -560,6 +560,7 @@ const Form = ({
onSubmit={(e) => console.log("onSubmit", e)}
onError={(e) => console.log("onError", e)}
ref={formRef}
+ className="relative"
/>
)}
diff --git a/packages/next-admin/src/components/LoaderRow.tsx b/packages/next-admin/src/components/LoaderRow.tsx
index 31230672..d389ac59 100644
--- a/packages/next-admin/src/components/LoaderRow.tsx
+++ b/packages/next-admin/src/components/LoaderRow.tsx
@@ -12,14 +12,15 @@ const LoadingRow = forwardRef<
return (
-
- {t("selector.loading")}
+
+ {t("selector.loading")}
);
});
diff --git a/packages/next-admin/src/components/inputs/MultiSelect/MultiSelectWidget.tsx b/packages/next-admin/src/components/inputs/MultiSelect/MultiSelectWidget.tsx
index 12040131..df80651c 100644
--- a/packages/next-admin/src/components/inputs/MultiSelect/MultiSelectWidget.tsx
+++ b/packages/next-admin/src/components/inputs/MultiSelect/MultiSelectWidget.tsx
@@ -1,7 +1,5 @@
import { RJSFSchema } from "@rjsf/utils";
import clsx from "clsx";
-import { PropsWithChildren, useRef } from "react";
-import { twMerge } from "tailwind-merge";
import DoubleArrow from "../../../assets/icons/DoubleArrow";
import { useForm } from "../../../context/FormContext";
import { useI18n } from "../../../context/I18nContext";
@@ -26,9 +24,10 @@ type Props = {
const MultiSelectWidget = (props: Props) => {
const formContext = useForm();
const { formData, onChange, options, name, schema } = props;
- const containerRef = useRef(null);
+ const containerRef = useCloseOnOutsideClick(() =>
+ formContext.setOpen(false, name)
+ );
const { t } = useI18n();
- useCloseOnOutsideClick(containerRef, () => formContext.setOpen(false, name));
const fieldOptions =
formContext.options?.model?.[formContext.resource!]?.edit?.fields?.[
name as Field
@@ -53,65 +52,58 @@ const MultiSelectWidget = (props: Props) => {
// @ts-expect-error
const fieldSortable = displayMode === "list" && !!fieldOptions?.orderField;
- const Select = ({
- children,
- className,
- }: PropsWithChildren>) => (
-
-
- {children}
-
+ const select = (
+
);
return (
-
+
{displayMode === "select" && (
-
+
+ {select}
+ {formData?.map(
+ (value: any, index: number) =>
+ value && (
+
onRemoveClick(value.value)}
+ deletable={!props.disabled}
+ />
+ )
+ )}
+ {!props.disabled && (
+
+
+
+ )}
+
)}
{displayMode === "list" && (
@@ -123,15 +115,16 @@ const MultiSelectWidget = (props: Props) => {
sortable={fieldSortable}
onUpdateFormData={onChange}
/>
-
+
+
)}
{displayMode === "table" && (
@@ -142,15 +135,16 @@ const MultiSelectWidget = (props: Props) => {
onRemoveClick={onRemoveClick}
deletable={!props.disabled}
/>
-
+
+
)}
diff --git a/packages/next-admin/src/components/inputs/SelectWidget.tsx b/packages/next-admin/src/components/inputs/SelectWidget.tsx
index ac8a1b69..7fa1ce9a 100644
--- a/packages/next-admin/src/components/inputs/SelectWidget.tsx
+++ b/packages/next-admin/src/components/inputs/SelectWidget.tsx
@@ -5,7 +5,7 @@ import {
import { WidgetProps } from "@rjsf/utils";
import clsx from "clsx";
import Link from "next/link";
-import { useMemo, useRef } from "react";
+import { useMemo } from "react";
import DoubleArrow from "../../assets/icons/DoubleArrow";
import { useConfig } from "../../context/ConfigContext";
import { useForm } from "../../context/FormContext";
@@ -28,8 +28,9 @@ const SelectWidget = ({
const enumOptions = options.enumOptions?.map(
(option: any) => option.value as Enumeration
);
- const containerRef = useRef
(null);
- useCloseOnOutsideClick(containerRef, () => formContext.setOpen(false, name));
+ const containerRef = useCloseOnOutsideClick(() => {
+ formContext.setOpen(false, name);
+ });
const { basePath } = useConfig();
diff --git a/packages/next-admin/src/components/inputs/Selector.tsx b/packages/next-admin/src/components/inputs/Selector.tsx
index 5c6c012b..976fa330 100644
--- a/packages/next-admin/src/components/inputs/Selector.tsx
+++ b/packages/next-admin/src/components/inputs/Selector.tsx
@@ -23,9 +23,7 @@ export const Selector = ({
}: SelectorProps) => {
const currentQuery = useRef("");
const searchInput = createRef();
- const loaderRowRef = useRef(null);
const { t } = useI18n();
- const [isLastRowReached, setIsLastRowReached] = useState(false);
const {
allOptions,
isPending,
@@ -76,35 +74,10 @@ export const Selector = ({
searchPage.current = 1;
currentQuery.current = e.target.value;
- setIsLastRowReached(false);
runSearch(currentQuery.current, true);
}, 300);
const containerRef = useRef(null);
- useEffect(() => {
- const observer = new IntersectionObserver(
- (entries) => {
- entries.forEach((entry) => {
- if (entry.isIntersecting) {
- setIsLastRowReached(true);
- observer.disconnect();
- }
- });
- },
- { threshold: 1 }
- );
-
- const current = loaderRowRef.current;
- if (current) {
- observer.observe(current);
- }
- return () => {
- if (current) {
- observer.unobserve(current);
- }
- };
- });
-
const onScroll = () => {
// No need to do an infinite scroll for enums
if (!containerRef.current || options) {
@@ -117,7 +90,8 @@ export const Selector = ({
const offset = height - scrollY;
if (
- offset === 0 &&
+ offset >= -100 &&
+ offset <= 0 &&
!isPending &&
optionsLeft.length < totalSearchedItems.current
) {
@@ -167,13 +141,11 @@ export const Selector = ({
{option.label}
))}
-
- {optionsLeft && optionsLeft.length === 0 && !isPending ? (
+ {isPending && }
+ {optionsLeft && optionsLeft.length === 0 && !isPending && (
No results found
- ) : (
- !isLastRowReached &&
)}
diff --git a/packages/next-admin/src/hooks/useCloseOnOutsideClick.ts b/packages/next-admin/src/hooks/useCloseOnOutsideClick.ts
index c8037e7f..76b45cb9 100644
--- a/packages/next-admin/src/hooks/useCloseOnOutsideClick.ts
+++ b/packages/next-admin/src/hooks/useCloseOnOutsideClick.ts
@@ -1,24 +1,23 @@
-import { useEffect } from "react";
+import { useEffect, useRef } from "react";
-function useCloseOnOutsideClick(
- ref: React.RefObject,
+const useCloseOnOutsideClick = (
close: () => void
-) {
- function onWindowClick(event: MouseEvent) {
+) => {
+ const ref: React.RefObject = useRef(null);
+ const onWindowClick = (event: MouseEvent) => {
if (ref.current && !ref.current.contains(event.target as Node)) {
close();
}
- }
- useEffect(
- () => {
- window.addEventListener("click", onWindowClick);
- return () => {
- window.removeEventListener("click", onWindowClick);
- };
- },
- // eslint-disable-next-line react-hooks/exhaustive-deps
- []
- );
-}
+ };
+
+ useEffect(() => {
+ window.addEventListener("click", onWindowClick);
+ return () => {
+ window.removeEventListener("click", onWindowClick);
+ };
+ }, []);
+
+ return ref;
+};
export default useCloseOnOutsideClick;