Merge pull request #57 from bknd-io/feat/elements-docs-and-custom

restructured elements for better customization
This commit is contained in:
dswbx
2025-01-25 09:17:39 +01:00
committed by GitHub
10 changed files with 211 additions and 40 deletions

View File

@@ -14,3 +14,6 @@ export { registries } from "modules/registries";
export type * from "./adapter";
export { Api, type ApiOptions } from "./Api";
export type { MediaFieldSchema } from "media/AppMedia";
export type { UserFieldSchema } from "auth/AppAuth";

View File

@@ -7,10 +7,23 @@ export type AuthScreenProps = {
action?: "login" | "register";
logo?: ReactNode;
intro?: ReactNode;
formOnly?: boolean;
};
export function AuthScreen({ method = "POST", action = "login", logo, intro }: AuthScreenProps) {
export function AuthScreen({
method = "POST",
action = "login",
logo,
intro,
formOnly
}: AuthScreenProps) {
const { strategies, basepath, loading } = useAuthStrategies();
const Form = <AuthForm auth={{ basepath, strategies }} method={method} action={action} />;
if (formOnly) {
if (loading) return null;
return Form;
}
return (
<div className="flex flex-1 flex-col select-none h-dvh w-dvw justify-center items-center bknd-admin">
@@ -25,7 +38,7 @@ export function AuthScreen({ method = "POST", action = "login", logo, intro }: A
<p className="text-primary/50">Enter your credentials below to get access.</p>
</div>
)}
<AuthForm auth={{ basepath, strategies }} method={method} action={action} />
{Form}
</div>
)}
</div>

View File

@@ -1,3 +1,4 @@
import { useAuthStrategies } from "../hooks/use-auth";
import { AuthForm } from "./AuthForm";
import { AuthScreen } from "./AuthScreen";
import { SocialLink } from "./SocialLink";
@@ -7,3 +8,5 @@ export const Auth = {
Form: AuthForm,
SocialLink: SocialLink
};
export { useAuthStrategies };

View File

@@ -1,2 +1,2 @@
export { Auth } from "./auth";
export * from "./auth";
export * from "./media";

View File

@@ -1,31 +1,32 @@
import type { RepoQuery, RepoQueryIn } from "data";
import type { RepoQueryIn } from "data";
import type { MediaFieldSchema } from "media/AppMedia";
import type { TAppMediaConfig } from "media/media-schema";
import { useId } from "react";
import { useApi, useBaseUrl, useEntityQuery, useInvalidate } from "ui/client";
import { type ReactNode, createContext, useContext, useId } from "react";
import { useApi, useEntityQuery, useInvalidate } from "ui/client";
import { useEvent } from "ui/hooks/use-event";
import { Dropzone, type DropzoneProps, type DropzoneRenderProps, type FileState } from "./Dropzone";
import { mediaItemsToFileStates } from "./helper";
export type DropzoneContainerProps = {
children?: (props: DropzoneRenderProps) => JSX.Element;
children?: ReactNode;
initialItems?: MediaFieldSchema[];
entity?: {
name: string;
id: number;
field: string;
};
media?: Pick<TAppMediaConfig, "entity_name" | "storage">;
query?: RepoQueryIn;
} & Partial<Pick<TAppMediaConfig, "basepath" | "entity_name" | "storage">> &
Partial<DropzoneProps>;
} & Omit<Partial<DropzoneProps>, "children" | "initialItems">;
const DropzoneContainerContext = createContext<DropzoneRenderProps>(undefined!);
export function DropzoneContainer({
initialItems,
basepath = "/api/media",
storage = {},
entity_name = "media",
media,
entity,
query,
children,
...props
}: DropzoneContainerProps) {
const id = useId();
@@ -33,10 +34,11 @@ export function DropzoneContainer({
const baseUrl = api.baseUrl;
const invalidate = useInvalidate();
const limit = query?.limit ? query?.limit : props.maxItems ? props.maxItems : 50;
console.log("dropzone:baseUrl", baseUrl);
const entity_name = (media?.entity_name ?? "media") as "media";
//console.log("dropzone:baseUrl", baseUrl);
const $q = useEntityQuery(
entity_name as "media",
entity_name,
undefined,
{
...query,
@@ -89,6 +91,18 @@ export function DropzoneContainer({
autoUpload
initialItems={_initialItems}
{...props}
/>
>
{children
? (props) => (
<DropzoneContainerContext.Provider value={props}>
{children}
</DropzoneContainerContext.Provider>
)
: undefined}
</Dropzone>
);
}
export function useDropzone() {
return useContext(DropzoneContainerContext);
}

View File

@@ -1,11 +1,14 @@
import { PreviewWrapperMemoized } from "./Dropzone";
import { DropzoneContainer } from "./DropzoneContainer";
import { DropzoneContainer, useDropzone } from "./DropzoneContainer";
export const Media = {
Dropzone: DropzoneContainer,
Preview: PreviewWrapperMemoized
Preview: PreviewWrapperMemoized,
useDropzone: useDropzone
};
export { useDropzone as useMediaDropzone };
export type {
PreviewComponentProps,
FileState,

View File

@@ -1,4 +1,4 @@
import { type DropzoneRenderProps, Media } from "ui/elements";
import { Media } from "ui/elements";
import { Scrollable } from "ui/layouts/AppShell/AppShell";
export default function DropzoneElementTest() {
@@ -12,7 +12,7 @@ export default function DropzoneElementTest() {
maxItems={1}
overwrite
>
{(props) => <CustomUserAvatarDropzone {...props} />}
<CustomUserAvatarDropzone />
</Media.Dropzone>
</div>
@@ -49,12 +49,13 @@ export default function DropzoneElementTest() {
);
}
function CustomUserAvatarDropzone({
wrapperRef,
inputProps,
state: { files, isOver, isOverAccepted, showPlaceholder },
actions: { openFileInput }
}: DropzoneRenderProps) {
function CustomUserAvatarDropzone() {
const {
wrapperRef,
inputProps,
state: { files, isOver, isOverAccepted, showPlaceholder },
actions: { openFileInput }
} = Media.useDropzone();
const file = files[0];
return (