added format command and added trailing commas to reduce conflicts

This commit is contained in:
dswbx
2025-02-26 20:06:03 +01:00
parent 88b5359f1c
commit 7743f71a11
414 changed files with 3622 additions and 3610 deletions

View File

@@ -21,7 +21,7 @@ export type BkndAdminProps = {
export default function Admin({
baseUrl: baseUrlOverride,
withProvider = false,
config
config,
}: BkndAdminProps) {
const Component = (
<BkndProvider adminOverride={config} fallback={<Skeleton theme={config?.color_scheme} />}>

View File

@@ -24,14 +24,14 @@ export type { TSchemaActions };
enum Fetching {
None = 0,
Schema = 1,
Secrets = 2
Secrets = 2,
}
export function BkndProvider({
includeSecrets = false,
adminOverride,
children,
fallback = null
fallback = null,
}: { includeSecrets?: boolean; children: any; fallback?: React.ReactNode } & Pick<
BkndContext,
"adminOverride"
@@ -59,7 +59,7 @@ export function BkndProvider({
const res = await api.system.readSchema({
config: true,
secrets: _includeSecrets
secrets: _includeSecrets,
});
if (!res.ok) {
@@ -80,13 +80,13 @@ export function BkndProvider({
schema: getDefaultSchema(),
config: getDefaultConfig(),
permissions: [],
fallback: true
fallback: true,
} as any);
if (adminOverride) {
newSchema.config.server.admin = {
...newSchema.config.server.admin,
...adminOverride
...adminOverride,
};
}

View File

@@ -4,7 +4,7 @@ import type { AppTheme } from "modules/server/AppServer";
import { createContext, useContext } from "react";
const ClientContext = createContext<{ baseUrl: string; api: Api }>({
baseUrl: undefined
baseUrl: undefined,
} as any);
export type ClientProviderProps = {
@@ -69,7 +69,7 @@ export function useBkndWindowContext(): BkndWindowContext {
return window.__BKND__ as any;
} else {
return {
logout_route: "/api/auth/logout"
logout_route: "/api/auth/logout",
};
}
}

View File

@@ -5,10 +5,10 @@ import { useApi } from "ui/client";
export const useApiQuery = <
Data,
RefineFn extends (data: ResponseObject<Data>) => unknown = (data: ResponseObject<Data>) => Data
RefineFn extends (data: ResponseObject<Data>) => unknown = (data: ResponseObject<Data>) => Data,
>(
fn: (api: Api) => FetchPromise<Data>,
options?: SWRConfiguration & { enabled?: boolean; refine?: RefineFn }
options?: SWRConfiguration & { enabled?: boolean; refine?: RefineFn },
) => {
const api = useApi();
const promise = fn(api);
@@ -23,7 +23,7 @@ export const useApiQuery = <
...swr,
promise,
key,
api
api,
};
};

View File

@@ -9,7 +9,7 @@ import { type Api, useApi } from "ui/client";
export class UseEntityApiError<Payload = any> extends Error {
constructor(
public response: ResponseObject<Payload>,
fallback?: string
fallback?: string,
) {
let message = fallback;
if ("error" in response) {
@@ -26,10 +26,10 @@ export class UseEntityApiError<Payload = any> extends Error {
export const useEntity = <
Entity extends keyof DB | string,
Id extends PrimaryFieldType | undefined = undefined,
Data = Entity extends keyof DB ? DB[Entity] : EntityData
Data = Entity extends keyof DB ? DB[Entity] : EntityData,
>(
entity: Entity,
id?: Id
id?: Id,
) => {
const api = useApi().data;
@@ -71,7 +71,7 @@ export const useEntity = <
throw new UseEntityApiError(res, `Failed to delete entity "${entity}"`);
}
return res;
}
},
};
};
@@ -80,7 +80,7 @@ export function makeKey(
api: ModuleApi,
entity: string,
id?: PrimaryFieldType,
query?: RepoQueryIn
query?: RepoQueryIn,
) {
return (
"/" +
@@ -93,12 +93,12 @@ export function makeKey(
export const useEntityQuery = <
Entity extends keyof DB | string,
Id extends PrimaryFieldType | undefined = undefined
Id extends PrimaryFieldType | undefined = undefined,
>(
entity: Entity,
id?: Id,
query?: RepoQueryIn,
options?: SWRConfiguration & { enabled?: boolean; revalidateOnMutate?: boolean }
options?: SWRConfiguration & { enabled?: boolean; revalidateOnMutate?: boolean },
) => {
const api = useApi().data;
const key = makeKey(api, entity as string, id, query);
@@ -109,13 +109,13 @@ export const useEntityQuery = <
const swr = useSWR<T>(options?.enabled === false ? null : key, fetcher as any, {
revalidateOnFocus: false,
keepPreviousData: true,
...options
...options,
});
const mutateAll = async () => {
const entityKey = makeKey(api, entity as string);
return mutate((key) => typeof key === "string" && key.startsWith(entityKey), undefined, {
revalidate: true
revalidate: true,
});
};
@@ -138,13 +138,13 @@ export const useEntityQuery = <
mutate: mutateAll,
mutateRaw: swr.mutate,
api,
key
key,
};
};
export async function mutateEntityCache<
Entity extends keyof DB | string,
Data = Entity extends keyof DB ? Omit<DB[Entity], "id"> : EntityData
Data = Entity extends keyof DB ? Omit<DB[Entity], "id"> : EntityData,
>(api: Api["data"], entity: Entity, id: PrimaryFieldType, partialData: Partial<Data>) {
function update(prev: any, partialNext: any) {
if (
@@ -171,23 +171,23 @@ export async function mutateEntityCache<
return update(data, partialData);
},
{
revalidate: false
}
revalidate: false,
},
);
}
export const useEntityMutate = <
Entity extends keyof DB | string,
Id extends PrimaryFieldType | undefined = undefined,
Data = Entity extends keyof DB ? Omit<DB[Entity], "id"> : EntityData
Data = Entity extends keyof DB ? Omit<DB[Entity], "id"> : EntityData,
>(
entity: Entity,
id?: Id,
options?: SWRConfiguration
options?: SWRConfiguration,
) => {
const { data, ...$q } = useEntityQuery<Entity, Id>(entity, id, undefined, {
...options,
enabled: false
enabled: false,
});
const _mutate = id
@@ -198,6 +198,6 @@ export const useEntityMutate = <
...$q,
mutate: _mutate as unknown as Id extends undefined
? (id: PrimaryFieldType, data: Partial<Data>) => Promise<void>
: (data: Partial<Data>) => Promise<void>
: (data: Partial<Data>) => Promise<void>,
};
};

View File

@@ -3,7 +3,7 @@ export {
useBkndWindowContext,
type ClientProviderProps,
useApi,
useBaseUrl
useBaseUrl,
} from "./ClientProvider";
export * from "./api/use-api";

View File

@@ -18,12 +18,12 @@ export function getSchemaActions({ api, setSchema, reloadSchema }: SchemaActions
action: string,
module: Module,
res: ResponseObject<ConfigUpdateResponse<Module>>,
path?: string
path?: string,
): Promise<boolean> {
const base: Partial<NotificationData> = {
id: "schema-" + [action, module, path].join("-"),
position: "top-right",
autoClose: 3000
autoClose: 3000,
};
if (res.success === true) {
@@ -35,8 +35,8 @@ export function getSchemaActions({ api, setSchema, reloadSchema }: SchemaActions
...prev,
config: {
...prev.config,
[module]: res.config
}
[module]: res.config,
},
};
});
}
@@ -45,7 +45,7 @@ export function getSchemaActions({ api, setSchema, reloadSchema }: SchemaActions
...base,
title: `Config updated: ${ucFirst(module)}`,
color: "blue",
message: `Operation ${action.toUpperCase()} at ${module}${path ? "." + path : ""}`
message: `Operation ${action.toUpperCase()} at ${module}${path ? "." + path : ""}`,
});
} else {
notifications.show({
@@ -54,7 +54,7 @@ export function getSchemaActions({ api, setSchema, reloadSchema }: SchemaActions
color: "red",
withCloseButton: true,
autoClose: false,
message: res.error ?? "Failed to complete config update"
message: res.error ?? "Failed to complete config update",
});
}
@@ -66,7 +66,7 @@ export function getSchemaActions({ api, setSchema, reloadSchema }: SchemaActions
set: async <Module extends keyof ModuleConfigs>(
module: keyof ModuleConfigs,
value: ModuleConfigs[Module],
force?: boolean
force?: boolean,
) => {
const res = await api.system.setConfig(module, value, force);
return await handleConfigUpdate("set", module, res);
@@ -74,7 +74,7 @@ export function getSchemaActions({ api, setSchema, reloadSchema }: SchemaActions
patch: async <Module extends keyof ModuleConfigs>(
module: Module,
path: string,
value: any
value: any,
): Promise<boolean> => {
const res = await api.system.patchConfig(module, path, value);
return await handleConfigUpdate("patch", module, res, path);
@@ -82,7 +82,7 @@ export function getSchemaActions({ api, setSchema, reloadSchema }: SchemaActions
overwrite: async <Module extends keyof ModuleConfigs>(
module: Module,
path: string,
value: any
value: any,
) => {
const res = await api.system.overwriteConfig(module, path, value);
return await handleConfigUpdate("overwrite", module, res, path);
@@ -94,6 +94,6 @@ export function getSchemaActions({ api, setSchema, reloadSchema }: SchemaActions
remove: async <Module extends keyof ModuleConfigs>(module: Module, path: string) => {
const res = await api.system.removeConfig(module, path);
return await handleConfigUpdate("remove", module, res, path);
}
},
};
}

View File

@@ -69,6 +69,6 @@ export const useAuth = (options?: { baseUrl?: string }): UseAuth => {
register,
logout,
setToken,
verify
verify,
};
};

View File

@@ -14,7 +14,7 @@ export function useBkndAuth() {
return true;
}
return false;
}
},
},
roles: {
add: async (name: string, data: any = {}) => {
@@ -31,8 +31,8 @@ export function useBkndAuth() {
return await bkndActions.remove("auth", `roles.${name}`);
}
return false;
}
}
},
},
};
const minimum_permissions = [
@@ -40,7 +40,7 @@ export function useBkndAuth() {
"system.access.api",
"system.config.read",
"system.config.read.secrets",
"system.build"
"system.build",
];
const $auth = {
roles: {
@@ -49,19 +49,21 @@ export function useBkndAuth() {
has_admin: Object.entries(config.auth.roles ?? {}).some(
([name, role]) =>
role.implicit_allow ||
minimum_permissions.every((p) => role.permissions?.includes(p))
)
minimum_permissions.every((p) => role.permissions?.includes(p)),
),
},
routes: {
settings: app.getSettingsPath(["auth"]),
listUsers: app.getAbsolutePath("/data/" + routes.data.entity.list(config.auth.entity_name))
}
listUsers: app.getAbsolutePath(
"/data/" + routes.data.entity.list(config.auth.entity_name),
),
},
};
return {
$auth,
config: config.auth,
schema: schema.auth,
actions
actions,
};
}

View File

@@ -8,7 +8,7 @@ import {
entitiesSchema,
entityFields,
fieldsSchema,
relationsSchema
relationsSchema,
} from "data/data-schema";
import { useBknd } from "ui/client/bknd";
import type { TSchemaActions } from "ui/client/schema/actions";
@@ -28,7 +28,7 @@ export function useBkndData() {
console.log("create entity", { data });
const validated = parse(entitiesSchema, data, {
skipMark: true,
forceParse: true
forceParse: true,
});
console.log("validated", validated);
// @todo: check for existing?
@@ -46,12 +46,12 @@ export function useBkndData() {
return await bkndActions.overwrite(
"data",
`entities.${entityName}.config`,
partial
partial,
);
},
fields: entityFieldActions(bkndActions, entityName)
fields: entityFieldActions(bkndActions, entityName),
};
}
},
},
relations: {
add: async (relation: TAppDataRelation) => {
@@ -59,12 +59,12 @@ export function useBkndData() {
const name = crypto.randomUUID();
const validated = parse(Type.Union(relationsSchema), relation, {
skipMark: true,
forceParse: true
forceParse: true,
});
console.log("validated", validated);
return await bkndActions.add("data", `relations.${name}`, validated);
}
}
},
},
};
const $data = {
entity: (name: string) => entities[name],
@@ -72,8 +72,8 @@ export function useBkndData() {
system: (name: string) => ({
any: entities[name]?.type === "system",
users: name === config.auth.entity_name,
media: name === config.media.entity_name
})
media: name === config.media.entity_name,
}),
};
return {
@@ -82,7 +82,7 @@ export function useBkndData() {
relations: app.relations,
config: config.data,
schema: schema.data,
actions
actions,
};
}
@@ -91,7 +91,7 @@ const modals = {
createEntity: () =>
bkndModals.open(bkndModals.ids.dataCreate, {
initialPath: ["entities", "entity"],
initialState: { action: "entity" }
initialState: { action: "entity" },
}),
createRelation: (entity?: string) =>
bkndModals.open(bkndModals.ids.dataCreate, {
@@ -99,9 +99,9 @@ const modals = {
initialState: {
action: "relation",
relations: {
create: [{ source: entity, type: "n:1" } as any]
}
}
create: [{ source: entity, type: "n:1" } as any],
},
},
}),
createMedia: (entity?: string) =>
bkndModals.open(bkndModals.ids.dataCreate, {
@@ -109,10 +109,10 @@ const modals = {
initialState: {
action: "template-media",
initial: {
entity
}
}
})
entity,
},
},
}),
};
function entityFieldActions(bkndActions: TSchemaActions, entityName: string) {
@@ -121,7 +121,7 @@ function entityFieldActions(bkndActions: TSchemaActions, entityName: string) {
console.log("create field", { name, field });
const validated = parse(fieldsSchema, field, {
skipMark: true,
forceParse: true
forceParse: true,
});
console.log("validated", validated);
return await bkndActions.add("data", `entities.${entityName}.fields.${name}`, validated);
@@ -132,12 +132,12 @@ function entityFieldActions(bkndActions: TSchemaActions, entityName: string) {
try {
const validated = parse(entityFields, fields, {
skipMark: true,
forceParse: true
forceParse: true,
});
const res = await bkndActions.overwrite(
"data",
`entities.${entityName}.fields`,
validated
validated,
);
console.log("res", res);
//bkndActions.set("data", "entities", fields);
@@ -149,6 +149,6 @@ function entityFieldActions(bkndActions: TSchemaActions, entityName: string) {
alert("An error occured, check console. There will be nice error handling soon.");
}
}
}
},
};
}

View File

@@ -13,8 +13,8 @@ export function useFlows() {
console.log("parsed", parsed);
const res = await bkndActions.add("flows", `flows.${name}`, parsed);
console.log("res", res);
}
}
},
},
};
return { flows: app.flows, config: config.flows, actions };

View File

@@ -13,8 +13,8 @@ export function useBkndMedia() {
}
return false;
}
}
},
},
};
const $media = {};
@@ -22,6 +22,6 @@ export function useBkndMedia() {
$media,
config: config.media,
schema: schema.media,
actions
actions,
};
}

View File

@@ -9,15 +9,15 @@ export function useBkndSystem() {
theme: {
set: async (scheme: "light" | "dark") => {
return await bkndActions.patch("server", "admin", {
color_scheme: scheme
color_scheme: scheme,
});
},
toggle: async () => {
return await bkndActions.patch("server", "admin", {
color_scheme: theme === "light" ? "dark" : "light"
color_scheme: theme === "light" ? "dark" : "light",
});
}
}
},
},
};
const $system = {};
@@ -26,7 +26,7 @@ export function useBkndSystem() {
config: config.server,
schema: schema.server,
theme,
actions
actions,
};
}
@@ -36,6 +36,6 @@ export function useBkndSystemTheme() {
return {
theme: $sys.theme,
set: $sys.actions.theme.set,
toggle: () => $sys.actions.theme.toggle()
toggle: () => $sys.actions.theme.toggle(),
};
}

View File

@@ -14,9 +14,9 @@ export function useSetTheme(initialTheme: AppTheme = "light") {
fetch("/api/system/config/patch/server/admin", {
method: "PATCH",
headers: {
"Content-Type": "application/json"
"Content-Type": "application/json",
},
body: JSON.stringify({ color_scheme: newTheme })
body: JSON.stringify({ color_scheme: newTheme }),
})
.then((res) => res.json())
.then((data) => {

View File

@@ -7,10 +7,10 @@ export function Context() {
<div>
{JSON.stringify(
{
baseurl
baseurl,
},
null,
2
2,
)}
</div>
);

View File

@@ -7,13 +7,13 @@ import { Link } from "ui/components/wouter/Link";
const sizes = {
small: "px-2 py-1.5 rounded-md gap-1 text-sm",
default: "px-3 py-2.5 rounded-md gap-1.5",
large: "px-4 py-3 rounded-md gap-2.5 text-lg"
large: "px-4 py-3 rounded-md gap-2.5 text-lg",
};
const iconSizes = {
small: 12,
default: 16,
large: 20
large: 20,
};
const styles = {
@@ -23,7 +23,7 @@ const styles = {
outline: "border border-primary/20 bg-transparent hover:bg-primary/5 link text-primary/80",
red: "dark:bg-red-950 dark:hover:bg-red-900 bg-red-100 hover:bg-red-200 link text-primary/70",
subtlered:
"dark:text-red-950 text-red-700 dark:hover:bg-red-900 bg-transparent hover:bg-red-50 link"
"dark:text-red-950 text-red-700 dark:hover:bg-red-900 bg-transparent hover:bg-red-50 link",
};
export type BaseProps = {
@@ -54,7 +54,7 @@ const Base = ({
"flex flex-row flex-nowrap items-center font-semibold disabled:opacity-50 cursor-pointer disabled:cursor-not-allowed transition-[opacity,background-color,color,border-color]",
sizes[size ?? "default"],
styles[variant ?? "default"],
props.className
props.className,
),
children: (
<>
@@ -66,7 +66,7 @@ const Base = ({
)}
{IconRight && <IconRight size={iconSize} {...iconProps} />}
</>
)
),
});
export type ButtonProps = React.ComponentPropsWithoutRef<"button"> & BaseProps;

View File

@@ -12,7 +12,7 @@ const styles = {
xs: { className: "p-0.5", size: 13 },
sm: { className: "p-0.5", size: 15 },
md: { className: "p-1", size: 18 },
lg: { className: "p-1.5", size: 22 }
lg: { className: "p-1.5", size: 22 },
} as const;
interface IconButtonProps extends ComponentPropsWithoutRef<"button"> {
@@ -38,5 +38,5 @@ export const IconButton = forwardRef<HTMLButtonElement, IconButtonProps>(
disabled={disabled}
/>
);
}
},
);

View File

@@ -10,7 +10,7 @@ import {
addEdge,
useEdgesState,
useNodesState,
useReactFlow
useReactFlow,
} from "@xyflow/react";
import { type ReactNode, useCallback, useEffect, useState } from "react";
import { useBkndSystemTheme } from "ui/client/schema/system/use-bknd-system";
@@ -127,9 +127,9 @@ export function Canvas({
data: { label: "" },
position: screenToFlowPosition({
x: clientX,
y: clientY
y: clientY,
}),
origin: [0.0, 0.0]
origin: [0.0, 0.0],
});
setNodes((nds) => nds.concat(newNode as any));
@@ -138,13 +138,13 @@ export function Canvas({
onDropNewNode({
id: newNode.id,
source: connectionState.fromNode.id,
target: newNode.id
})
)
target: newNode.id,
}),
),
);
}
},
[screenToFlowPosition]
[screenToFlowPosition],
);
//console.log("edges1", edges);
@@ -163,12 +163,12 @@ export function Canvas({
: ""
}
proOptions={{
hideAttribution: true
hideAttribution: true,
}}
fitView
fitViewOptions={{
maxZoom: 1.5,
...props.fitViewOptions
...props.fitViewOptions,
}}
nodeDragThreshold={25}
panOnScrollSpeed={1}

View File

@@ -14,7 +14,7 @@ export function DefaultNode({ selected, children, className, ...props }: TDefaul
className={twMerge(
"relative w-80 shadow-lg rounded-lg bg-background",
selected && "outline outline-blue-500/25",
className
className,
)}
>
{children}
@@ -31,7 +31,7 @@ const Header: React.FC<TDefaultNodeHeaderProps> = ({ className, label, children,
{...props}
className={twMerge(
"flex flex-row bg-primary/15 justify-center items-center rounded-tl-lg rounded-tr-lg py-1 px-2 drag-handle",
className
className,
)}
>
{children ? (

View File

@@ -29,7 +29,7 @@ export const layoutWithDagre = ({ nodes, edges, graph }: LayoutProps) => {
nodes.forEach((node) => {
dagreGraph.setNode(node.id, {
width: node.width,
height: node.height
height: node.height,
});
});
@@ -45,9 +45,9 @@ export const layoutWithDagre = ({ nodes, edges, graph }: LayoutProps) => {
return {
...node,
x: position.x - (node.width ?? 0) / 2,
y: position.y - (node.height ?? 0) / 2
y: position.y - (node.height ?? 0) / 2,
};
}),
edges
edges,
};
};

View File

@@ -33,7 +33,7 @@ const Wrapper = ({ children, className, ...props }: ComponentPropsWithoutRef<"di
{...props}
className={twMerge(
"flex flex-row bg-lightest border ring-2 ring-muted/5 border-muted rounded-full items-center p-1",
className
className,
)}
>
{children}
@@ -70,7 +70,7 @@ const Text = forwardRef<any, ComponentPropsWithoutRef<"span"> & { mono?: boolean
>
{children}
</span>
)
),
);
Panel.Wrapper = Wrapper;

View File

@@ -23,7 +23,7 @@ export default function CodeEditor({
? {
...(typeof basicSetup === "object" ? basicSetup : {}),
highlightActiveLine: false,
highlightActiveLineGutter: false
highlightActiveLineGutter: false,
}
: basicSetup;

View File

@@ -10,7 +10,7 @@ export function JsonEditor({ editable, className, ...props }: CodeEditorProps) {
className={twMerge(
"flex w-full border border-muted",
!editable && "opacity-70",
className
className,
)}
editable={editable}
_extensions={{ json: true }}

View File

@@ -9,7 +9,7 @@ export const JsonViewer = ({
expand = 0,
showSize = false,
showCopy = false,
className
className,
}: {
json: object;
title?: string;
@@ -61,7 +61,7 @@ export const JsonViewer = ({
"text-zinc-400 font-mono font-bold text-lg after:content-['▾'] mr-1.5",
expandIcon:
"text-zinc-400 font-mono font-bold text-lg after:content-['▸'] mr-1.5",
noQuotesForStringValues: false
noQuotesForStringValues: false,
} as any
}
/>

View File

@@ -80,7 +80,7 @@ const filters = [
{ label: "url_encode" },
{ label: "where" },
{ label: "where_exp" },
{ label: "xml_escape" }
{ label: "xml_escape" },
];
const tags = [
@@ -103,7 +103,7 @@ const tags = [
{ label: "render" },
{ label: "tablerow" },
{ label: "unless" },
{ label: "when" }
{ label: "when" },
];
export function LiquidJsEditor({ editable, ...props }: CodeEditorProps) {
@@ -112,11 +112,11 @@ export function LiquidJsEditor({ editable, ...props }: CodeEditorProps) {
<CodeEditor
className={twMerge(
"flex w-full border border-muted bg-white rounded-lg",
!editable && "opacity-70"
!editable && "opacity-70",
)}
editable={editable}
_extensions={{
liquid: { filters, tags }
liquid: { filters, tags },
}}
{...props}
/>

View File

@@ -22,7 +22,7 @@ const Base: React.FC<AlertProps> = ({
{...props}
className={twMerge(
"flex flex-row items-center dark:bg-amber-300/20 bg-amber-200 p-4",
className
className,
)}
>
<p>
@@ -52,5 +52,5 @@ export const Alert = {
Warning,
Exception,
Success,
Info
Info,
};

View File

@@ -15,7 +15,7 @@ export const Empty: React.FC<EmptyProps> = ({
description = "Check back later my friend.",
primary,
secondary,
className
className,
}) => (
<div className={twMerge("flex flex-col h-full w-full justify-center items-center", className)}>
<div className="flex flex-col gap-3 items-center max-w-80">

View File

@@ -14,5 +14,5 @@ const Warning = ({ className, ...props }: IconProps) => (
);
export const Icon = {
Warning
Warning,
};

View File

@@ -11,7 +11,7 @@ export function Logo({
const dim = {
width: Math.round(578 * scale),
height: Math.round(188 * scale)
height: Math.round(188 * scale),
} as const;
return (

View File

@@ -20,5 +20,5 @@ const MissingPermission = ({
export const Message = {
NotFound,
NotAllowed,
MissingPermission
MissingPermission,
};

View File

@@ -23,7 +23,7 @@ export function FloatingSelect({ data, label, description }: FloatingSelectProps
key={item}
className={twMerge(
"transition-colors duration-100 px-2.5 py-2 leading-none rounded-lg text-md",
active === index && "text-white"
active === index && "text-white",
)}
ref={setControlRef(index)}
onClick={() => setActive(index)}

View File

@@ -26,5 +26,5 @@ export const BooleanInputMantine = forwardRef<HTMLInputElement, React.ComponentP
/>
</div>
);
}
},
);

View File

@@ -10,7 +10,7 @@ import {
useEffect,
useImperativeHandle,
useRef,
useState
useState,
} from "react";
import { TbCalendar, TbChevronDown, TbInfoCircle } from "react-icons/tb";
import { twMerge } from "tailwind-merge";
@@ -32,7 +32,7 @@ export const Group = <E extends ElementType = "div">({
as === "fieldset" && "border border-primary/10 p-3 rounded-md",
as === "fieldset" && error && "border-red-500",
error && "text-red-500",
props.className
props.className,
)}
/>
);
@@ -93,7 +93,7 @@ export const Input = forwardRef<HTMLInputElement, React.ComponentProps<"input">>
disabledOrReadonly && "bg-muted/50 text-primary/50",
!disabledOrReadonly &&
"focus:bg-muted focus:outline-none focus:ring-2 focus:ring-zinc-500 focus:border-transparent transition-all",
props.className
props.className,
)}
/>
);
@@ -108,11 +108,11 @@ export const Textarea = forwardRef<HTMLTextAreaElement, React.ComponentProps<"te
ref={ref}
className={twMerge(
"bg-muted/40 min-h-11 rounded-md py-2.5 px-4 focus:bg-muted outline-none focus:outline-none focus:ring-2 focus:ring-zinc-500 focus:border-transparent transition-all disabled:bg-muted/50 disabled:text-primary/50",
props.className
props.className,
)}
/>
);
}
},
);
export const DateInput = forwardRef<HTMLInputElement, React.ComponentProps<"input">>(
@@ -145,7 +145,7 @@ export const DateInput = forwardRef<HTMLInputElement, React.ComponentProps<"inpu
/>
</div>
);
}
},
);
export const BooleanInput = forwardRef<HTMLInputElement, React.ComponentProps<"input">>(
@@ -174,23 +174,23 @@ export const BooleanInput = forwardRef<HTMLInputElement, React.ComponentProps<"i
/>
</div>
);
}
},
);
export type SwitchValue = boolean | 1 | 0 | "true" | "false" | "on" | "off";
const SwitchSizes = {
xs: {
root: "h-5 w-8",
thumb: "data-[state=checked]:left-[calc(100%-1rem)]"
thumb: "data-[state=checked]:left-[calc(100%-1rem)]",
},
sm: {
root: "h-6 w-10",
thumb: "data-[state=checked]:left-[calc(100%-1.25rem)]"
thumb: "data-[state=checked]:left-[calc(100%-1.25rem)]",
},
md: {
root: "h-7 w-12",
thumb: "data-[state=checked]:left-[calc(100%-1.5rem)]"
}
thumb: "data-[state=checked]:left-[calc(100%-1.5rem)]",
},
};
export const Switch = forwardRef<
@@ -210,7 +210,7 @@ export const Switch = forwardRef<
className={clsx(
"relative cursor-pointer rounded-full bg-muted border-2 border-transparent outline-none ring-1 dark:ring-primary/10 ring-primary/20 data-[state=checked]:ring-primary/60 data-[state=checked]:bg-primary/60 appearance-none transition-colors hover:bg-muted/80",
SwitchSizes[props.size ?? "md"].root,
props.disabled && "opacity-50 !cursor-not-allowed"
props.disabled && "opacity-50 !cursor-not-allowed",
)}
onCheckedChange={(bool) => {
console.log("setting", bool);
@@ -229,7 +229,7 @@ export const Switch = forwardRef<
<RadixSwitch.Thumb
className={clsx(
"absolute top-0 left-0 h-full aspect-square rounded-full bg-primary/30 data-[state=checked]:bg-background transition-[left,right] duration-100 border border-muted",
SwitchSizes[props.size ?? "md"].thumb
SwitchSizes[props.size ?? "md"].thumb,
)}
/>
</RadixSwitch.Root>
@@ -250,7 +250,7 @@ export const Select = forwardRef<
"bg-muted/40 focus:bg-muted rounded-md py-2.5 px-4 outline-none focus:outline-none focus:ring-2 focus:ring-zinc-500 focus:border-transparent transition-all disabled:bg-muted/50 disabled:text-primary/50",
"appearance-none h-11 w-full",
!props.multiple && "border-r-8 border-r-transparent",
props.className
props.className,
)}
>
{options ? (

View File

@@ -1,7 +1,7 @@
import {
Input,
SegmentedControl as MantineSegmentedControl,
type SegmentedControlProps as MantineSegmentedControlProps
type SegmentedControlProps as MantineSegmentedControlProps,
} from "@mantine/core";
type SegmentedControlProps = MantineSegmentedControlProps & {

View File

@@ -1,6 +1,6 @@
import {
NumberInput as $NumberInput,
type NumberInputProps as $NumberInputProps
type NumberInputProps as $NumberInputProps,
} from "@mantine/core";
import { type FieldValues, type UseControllerProps, useController } from "react-hook-form";
@@ -18,13 +18,13 @@ export function MantineNumberInput<T extends FieldValues>({
}: MantineNumberInputProps<T>) {
const {
field: { value, onChange: fieldOnChange, ...field },
fieldState
fieldState,
} = useController<T>({
name,
control,
defaultValue,
rules,
shouldUnregister
shouldUnregister,
});
return (

View File

@@ -2,7 +2,7 @@ import {
Radio as $Radio,
RadioGroup as $RadioGroup,
type RadioGroupProps as $RadioGroupProps,
type RadioProps as $RadioProps
type RadioProps as $RadioProps,
} from "@mantine/core";
import { type FieldValues, type UseControllerProps, useController } from "react-hook-form";
@@ -19,13 +19,13 @@ export function MantineRadio<T extends FieldValues>({
...props
}: RadioProps<T>) {
const {
field: { value, onChange: fieldOnChange, ...field }
field: { value, onChange: fieldOnChange, ...field },
} = useController<T>({
name,
control,
defaultValue,
rules,
shouldUnregister
shouldUnregister,
});
return (
@@ -55,13 +55,13 @@ function RadioGroup<T extends FieldValues>({
}: RadioGroupProps<T>) {
const {
field: { value, onChange: fieldOnChange, ...field },
fieldState
fieldState,
} = useController<T>({
name,
control,
defaultValue,
rules,
shouldUnregister
shouldUnregister,
});
return (

View File

@@ -1,7 +1,7 @@
import {
SegmentedControl as $SegmentedControl,
type SegmentedControlProps as $SegmentedControlProps,
Input
Input,
} from "@mantine/core";
import { type FieldValues, type UseControllerProps, useController } from "react-hook-form";
@@ -26,13 +26,13 @@ export function MantineSegmentedControl<T extends FieldValues>({
...props
}: MantineSegmentedControlProps<T>) {
const {
field: { value, onChange: fieldOnChange, ...field }
field: { value, onChange: fieldOnChange, ...field },
} = useController<T>({
name,
control,
defaultValue,
rules,
shouldUnregister
shouldUnregister,
});
return (

View File

@@ -16,13 +16,13 @@ export function MantineSelect<T extends FieldValues>({
}: MantineSelectProps<T>) {
const {
field: { value, onChange: fieldOnChange, ...field },
fieldState
fieldState,
} = useController<T>({
name,
control,
defaultValue,
rules,
shouldUnregister
shouldUnregister,
});
return (
@@ -34,8 +34,8 @@ export function MantineSelect<T extends FieldValues>({
...new Event("change", { bubbles: true, cancelable: true }),
target: {
value: e,
name: field.name
}
name: field.name,
},
});
// @ts-ignore
onChange?.(e);

View File

@@ -15,13 +15,13 @@ export function MantineSwitch<T extends FieldValues>({
}: SwitchProps<T>) {
const {
field: { value, onChange: fieldOnChange, ...field },
fieldState
fieldState,
} = useController<T>({
name,
control,
defaultValue,
rules,
shouldUnregister
shouldUnregister,
});
return (

View File

@@ -39,7 +39,7 @@ const Root = ({ path = "", children }: AnyOfFieldRootProps) => {
lib,
pointer,
value: { matchedIndex, schemas },
schema
schema,
} = useDerivedFieldContext(path, (ctx) => {
const [matchedIndex, schemas = []] = getMultiSchemaMatched(ctx.schema, ctx.value);
return { matchedIndex, schemas };
@@ -58,7 +58,7 @@ const Root = ({ path = "", children }: AnyOfFieldRootProps) => {
const options = schemas.map((s, i) => s.title ?? `Option ${i + 1}`);
const selectSchema = {
type: "string",
enum: options
enum: options,
} satisfies JsonSchema;
const selectedSchema = selected !== null ? (schemas[selected] as JsonSchema) : undefined;
@@ -69,7 +69,7 @@ const Root = ({ path = "", children }: AnyOfFieldRootProps) => {
selectedSchema,
schema,
schemas,
selected
selected,
};
}, [selected]);
@@ -80,7 +80,7 @@ const Root = ({ path = "", children }: AnyOfFieldRootProps) => {
...context,
select,
path,
errors
errors,
}}
>
{children}
@@ -130,7 +130,7 @@ export const AnyOf = {
Root,
Select,
Field,
useContext: useAnyOfContext
useContext: useAnyOfContext,
};
export const AnyOfField = (props: Omit<AnyOfFieldRootProps, "children">) => {

View File

@@ -72,7 +72,7 @@ const ArrayItem = memo(({ path, index, schema }: any) => {
const DeleteButton = useMemo(
() => <IconButton Icon={IconTrash} onClick={() => handleDelete(itemPath)} size="sm" />,
[itemPath]
[itemPath],
);
return (
@@ -95,7 +95,7 @@ const ArrayIterator = memo(
({ name, children }: any) => {
return children(useFormValue(name));
},
(prev, next) => prev.value?.length === next.value?.length
(prev, next) => prev.value?.length === next.value?.length,
);
const ArrayAdd = ({ schema, path }: { schema: JsonSchema; path: string }) => {
@@ -117,11 +117,11 @@ const ArrayAdd = ({ schema, path }: { schema: JsonSchema; path: string }) => {
return (
<Dropdown
dropdownWrapperProps={{
className: "min-w-0"
className: "min-w-0",
}}
items={itemsMultiSchema.map((s, i) => ({
label: s!.title ?? `Option ${i + 1}`,
onClick: () => handleAdd(ctx.lib.getTemplate(undefined, s!))
onClick: () => handleAdd(ctx.lib.getTemplate(undefined, s!)),
}))}
onClickItem={console.log}
>

View File

@@ -92,7 +92,9 @@ export const FieldComponent = ({
// allow override
value: typeof _props.value !== "undefined" ? _props.value : value,
placeholder:
(_props.placeholder ?? typeof schema.default !== "undefined") ? String(schema.default) : ""
(_props.placeholder ?? typeof schema.default !== "undefined")
? String(schema.default)
: "",
};
if (schema.enum) {
@@ -103,7 +105,7 @@ export const FieldComponent = ({
const additional = {
min: schema.minimum,
max: schema.maximum,
step: schema.multipleOf
step: schema.multipleOf,
};
return (
@@ -133,7 +135,7 @@ export const FieldComponent = ({
const date = new Date(e.target.value);
props.onChange?.({
// @ts-ignore
target: { value: date.toISOString() }
target: { value: date.toISOString() },
});
}}
/>
@@ -147,7 +149,7 @@ export const FieldComponent = ({
const additional = {
maxLength: schema.maxLength,
minLength: schema.minLength,
pattern: schema.pattern
pattern: schema.pattern,
} as any;
if (schema.format) {

View File

@@ -7,7 +7,7 @@ import * as Formy from "ui/components/form/Formy";
import {
useFormContext,
useFormError,
useFormValue
useFormValue,
} from "ui/components/form/json-schema-form/Form";
import { Popover } from "ui/components/overlay/Popover";
import { getLabel } from "./utils";
@@ -79,7 +79,7 @@ export function FieldWrapper({
{Children.count(children) === 1 && isValidElement(children)
? cloneElement(children, {
// @ts-ignore
list: examples.length > 0 ? examplesId : undefined
list: examples.length > 0 ? examplesId : undefined,
})
: children}
{examples.length > 0 && (
@@ -100,7 +100,7 @@ export function FieldWrapper({
const FieldDebug = ({
name,
schema,
required
required,
}: Pick<FieldwrapperProps, "name" | "schema" | "required">) => {
const { options } = useFormContext();
if (!options?.debug) return null;
@@ -111,7 +111,7 @@ const FieldDebug = ({
<div className="absolute top-0 right-0">
<Popover
overlayProps={{
className: "max-w-none"
className: "max-w-none",
}}
position="bottom-end"
target={({ toggle }) => (
@@ -122,7 +122,7 @@ const FieldDebug = ({
value,
required,
schema,
errors
errors,
}}
expand={6}
/>

View File

@@ -4,7 +4,7 @@ import {
getDefaultStore,
useAtom,
useAtomValue,
useSetAtom
useSetAtom,
} from "jotai";
import { selectAtom } from "jotai/utils";
import { Draft2019, type JsonError, type JsonSchema as LibJsonSchema } from "json-schema-library";
@@ -20,7 +20,7 @@ import {
useContext,
useEffect,
useMemo,
useRef
useRef,
} from "react";
import { JsonViewer } from "ui/components/code/JsonViewer";
import { useEvent } from "ui/hooks/use-event";
@@ -32,7 +32,7 @@ import {
omitSchema,
pathToPointer,
prefixPath,
prefixPointer
prefixPointer,
} from "./utils";
export type JSONSchema = Exclude<$JSONSchema, boolean>;
@@ -67,7 +67,7 @@ FormContext.displayName = "FormContext";
export function Form<
const Schema extends JSONSchema,
const Data = Schema extends JSONSchema ? FromSchema<Schema> : any
const Data = Schema extends JSONSchema ? FromSchema<Schema> : any,
>({
schema: _schema,
initialValues: _initialValues,
@@ -101,7 +101,7 @@ export function Form<
dirty: false,
submitting: false,
errors: [] as JsonError[],
data: initialValues
data: initialValues,
});
}, [initialValues]);
const setFormState = useSetAtom(_formStateAtom);
@@ -188,9 +188,9 @@ export function Form<
lib,
options,
root: "",
path: ""
path: "",
}),
[schema, initialValues]
[schema, initialValues],
) as any;
return (
@@ -236,7 +236,7 @@ export function FormContextOverride({
const context = {
...ctx,
...overrides,
...additional
...additional,
};
console.log("context", context);
@@ -256,12 +256,12 @@ export function useFormValue(name: string, opts?: { strict?: boolean }) {
const pointer = pathToPointer(prefixedName);
return {
value: getPath(state.data, prefixedName),
errors: state.errors.filter((error) => error.data.pointer.startsWith(pointer))
errors: state.errors.filter((error) => error.data.pointer.startsWith(pointer)),
};
},
[name]
[name],
),
isEqual
isEqual,
);
return useAtom(selected)[0];
}
@@ -280,16 +280,16 @@ export function useFormError(name: string, opt?: { strict?: boolean; debug?: boo
: error.data.pointer.startsWith(pointer);
});
},
[name]
[name],
),
isEqual
isEqual,
);
return useAtom(selected)[0];
}
export function useFormStateSelector<Data = any, Reduced = Data>(
selector: (state: FormState<Data>) => Reduced,
deps: any[] = []
deps: any[] = [],
): Reduced {
const { _formStateAtom } = useFormContext();
const selected = selectAtom(_formStateAtom, useCallback(selector, deps), isEqual);
@@ -309,7 +309,7 @@ export function useDerivedFieldContext<Data = any, Reduced = undefined>(
},
Reduced
>,
_schema?: JSONSchema
_schema?: JSONSchema,
): FormContext<Data> & {
value: Reduced;
pointer: string;
@@ -338,29 +338,29 @@ export function useDerivedFieldContext<Data = any, Reduced = undefined>(
root,
schema: fieldSchema as LibJsonSchema,
pointer,
required
required,
};
const derived = deriveFn?.({ ...context, _formStateAtom, lib, value });
return {
...context,
value: derived
value: derived,
};
},
[path, schema ?? {}, root]
[path, schema ?? {}, root],
),
isEqual
isEqual,
);
return {
...useAtomValue(selected),
_formStateAtom,
lib
lib,
} as any;
}
export function Subscribe<Data = any, Refined = Data>({
children,
selector
selector,
}: {
children: (state: Refined) => ReactNode;
selector?: SelectorFn<FormState<Data>, Refined>;

View File

@@ -102,7 +102,7 @@ export function getMultiSchema(schema: JsonSchema): JsonSchema[] | undefined {
export function getMultiSchemaMatched(
schema: JsonSchema,
data: any
data: any,
): [number, JsonSchema[], JsonSchema | undefined] {
const multiSchema = getMultiSchema(schema);
//console.log("getMultiSchemaMatched", schema, data, multiSchema);
@@ -124,7 +124,7 @@ export function omitSchema<Given extends JSONSchema>(_schema: Given, keys: strin
const updated = {
...schema,
properties: omitKeys(schema.properties, keys)
properties: omitKeys(schema.properties, keys),
};
if (updated.required) {
updated.required = updated.required.filter((key) => !keys.includes(key as any));

View File

@@ -40,7 +40,7 @@ export const JsonSchemaForm = forwardRef<JsonSchemaFormRef, JsonSchemaFormProps>
cleanOnChange,
...props
},
ref
ref,
) => {
const formRef = useRef<Form<any, RJSFSchema, any>>(null);
const id = useId();
@@ -67,32 +67,32 @@ export const JsonSchemaForm = forwardRef<JsonSchemaFormRef, JsonSchemaFormProps>
formData: () => value,
validateForm: () => formRef.current!.validateForm(),
silentValidate: () => isValid(value),
cancel: () => formRef.current!.reset()
cancel: () => formRef.current!.reset(),
}),
[value]
[value],
);
const _uiSchema: UiSchema = {
...uiSchema,
"ui:globalOptions": {
...uiSchema?.["ui:globalOptions"],
enableMarkdownInDescription: true
enableMarkdownInDescription: true,
},
"ui:submitButtonOptions": {
norender: true
}
norender: true,
},
};
const _fields: any = {
...Fields,
...fields
...fields,
};
const _templates: any = {
...Templates,
...templates
...templates,
};
const _widgets: any = {
...Widgets,
...widgets
...widgets,
};
//console.log("schema", schema, removeTitleFromSchema(schema));
@@ -115,7 +115,7 @@ export const JsonSchemaForm = forwardRef<JsonSchemaFormRef, JsonSchemaFormProps>
validator={validator as any}
/>
);
}
},
);
function removeTitleFromSchema(schema: any): any {
// Create a deep copy of the schema using lodash

View File

@@ -9,7 +9,7 @@ import type {
StrictRJSFSchema,
UiSchema,
ValidationData,
ValidatorType
ValidatorType,
} from "@rjsf/utils";
import { toErrorSchema } from "@rjsf/utils";
import { get } from "lodash-es";
@@ -49,7 +49,7 @@ const validate = true;
export class JsonSchemaValidator<
T = any,
S extends StrictRJSFSchema = RJSFSchema,
F extends FormContextType = any
F extends FormContextType = any,
> implements ValidatorType
{
// @ts-ignore
@@ -69,7 +69,7 @@ export class JsonSchemaValidator<
schema: S,
customValidate?: CustomValidator,
transformErrors?: ErrorTransformer,
uiSchema?: UiSchema
uiSchema?: UiSchema,
): ValidationData<T> {
if (!validate) return { errors: [], errorSchema: {} as any };
@@ -80,7 +80,7 @@ export class JsonSchemaValidator<
schema,
customValidate,
transformErrors,
uiSchema
uiSchema,
);
const { errors } = this.rawValidation(schema, formData);
debug && console.log("errors", { errors });
@@ -97,14 +97,14 @@ export class JsonSchemaValidator<
message: errorText,
property: "." + error.instanceLocation.replace(/^#\/?/, "").split("/").join("."),
schemaPath: error.keywordLocation,
stack: error.error
stack: error.error,
};
});
debug && console.log("transformed", transformedErrors);
return {
errors: transformedErrors,
errorSchema: toErrorSchema(transformedErrors)
errorSchema: toErrorSchema(transformedErrors),
} as any;
}

View File

@@ -12,7 +12,7 @@ import {
getDiscriminatorFieldFromSchema,
getUiOptions,
getWidget,
mergeSchemas
mergeSchemas,
} from "@rjsf/utils";
import { get, isEmpty, omit } from "lodash-es";
import { Component } from "react";
@@ -35,7 +35,7 @@ type AnyOfFieldState<S extends StrictRJSFSchema = RJSFSchema> = {
class MultiSchemaField<
T = any,
S extends StrictRJSFSchema = RJSFSchema,
F extends FormContextType = any
F extends FormContextType = any,
> extends Component<FieldProps<T, S, F>, AnyOfFieldState<S>> {
/** Constructs an `AnyOfField` with the given `props` to initialize the initially selected option in state
*
@@ -47,7 +47,7 @@ class MultiSchemaField<
const {
formData,
options,
registry: { schemaUtils }
registry: { schemaUtils },
} = this.props;
// cache the retrieved options in state in case they have $refs to save doing it later
//console.log("multi schema", { formData, options, props });
@@ -55,7 +55,7 @@ class MultiSchemaField<
this.state = {
retrievedOptions,
selectedOption: this.getMatchingOption(0, formData, retrievedOptions)
selectedOption: this.getMatchingOption(0, formData, retrievedOptions),
};
}
@@ -67,18 +67,18 @@ class MultiSchemaField<
*/
override componentDidUpdate(
prevProps: Readonly<FieldProps<T, S, F>>,
prevState: Readonly<AnyOfFieldState>
prevState: Readonly<AnyOfFieldState>,
) {
const { formData, options, idSchema } = this.props;
const { selectedOption } = this.state;
let newState = this.state;
if (!deepEquals(prevProps.options, options)) {
const {
registry: { schemaUtils }
registry: { schemaUtils },
} = this.props;
// re-cache the retrieved options in state in case they have $refs to save doing it later
const retrievedOptions = options.map((opt: S) =>
schemaUtils.retrieveSchema(opt, formData)
schemaUtils.retrieveSchema(opt, formData),
);
newState = { selectedOption, retrievedOptions };
}
@@ -104,7 +104,7 @@ class MultiSchemaField<
getMatchingOption(selectedOption: number, formData: T | undefined, options: S[]) {
const {
schema,
registry: { schemaUtils }
registry: { schemaUtils },
} = this.props;
const discriminator = getDiscriminatorFieldFromSchema<S>(schema);
@@ -112,7 +112,7 @@ class MultiSchemaField<
formData,
options,
selectedOption,
discriminator
discriminator,
);
return option;
}
@@ -143,7 +143,7 @@ class MultiSchemaField<
newFormData = schemaUtils.getDefaultFormState(
newOption,
newFormData,
"excludeObjectChildren"
"excludeObjectChildren",
) as T;
}
onChange(newFormData, undefined, this.getFieldId());
@@ -169,7 +169,7 @@ class MultiSchemaField<
registry,
schema,
uiSchema,
readonly
readonly,
} = this.props;
const { widgets, fields, translateString, globalUiOptions, schemaUtils } = registry;
@@ -241,7 +241,7 @@ class MultiSchemaField<
return {
label:
uiTitle || translateString(translateEnum, translateParams.concat(String(index + 1))),
value: index
value: index,
};
});
@@ -255,8 +255,8 @@ class MultiSchemaField<
...optionUiSchema,
"ui:options": {
...optionUiSchema?.["ui:options"],
hideLabel: true
}
hideLabel: true,
},
}}
/>
);
@@ -265,7 +265,7 @@ class MultiSchemaField<
<div
className={twMerge(
"panel multischema flex",
flexDirection === "row" ? "flex-row gap-3" : "flex-col gap-2"
flexDirection === "row" ? "flex-row gap-3" : "flex-col gap-2",
)}
>
<div className="flex flex-row gap-2 items-center panel-select">

View File

@@ -6,5 +6,5 @@ export const fields = {
AnyOfField: MultiSchemaField,
OneOfField: MultiSchemaField,
JsonField,
LiquidJsField
LiquidJsField,
};

View File

@@ -5,8 +5,8 @@ export type { JsonSchemaFormProps, JsonSchemaFormRef };
const Module = lazy(() =>
import("./JsonSchemaForm").then((m) => ({
default: m.JsonSchemaForm
}))
default: m.JsonSchemaForm,
})),
);
export const JsonSchemaForm = forwardRef<JsonSchemaFormRef, JsonSchemaFormProps>((props, ref) => {

View File

@@ -1,264 +1,265 @@
*,
*::before,
*::after {
box-sizing: border-box;
box-sizing: border-box;
}
.json-form {
@apply flex flex-col flex-grow;
@apply flex flex-col flex-grow;
/* dirty fix preventing the first fieldset to wrap */
&.mute-root {
& > div > div > div > fieldset:first-child {
@apply border-none p-0;
}
}
/* dirty fix preventing the first fieldset to wrap */
&.mute-root {
& > div > div > div > fieldset:first-child {
@apply border-none p-0;
}
}
&:not(.fieldset-alternative) {
fieldset {
@apply flex flex-grow flex-col gap-3.5 border border-solid border-muted p-3 rounded;
&:not(.fieldset-alternative) {
fieldset {
@apply flex flex-grow flex-col gap-3.5 border border-solid border-muted p-3 rounded;
.title-field {
@apply bg-primary/10 px-3 text-sm font-medium py-1 rounded-full;
align-self: flex-start;
.title-field {
@apply bg-primary/10 px-3 text-sm font-medium py-1 rounded-full;
align-self: flex-start;
}
}
}
/* alternative */
&.fieldset-alternative {
fieldset {
@apply flex flex-grow flex-col gap-3.5;
&:has(> legend) {
@apply mt-3 border-l-4 border-solid border-muted/50 p-3 pb-0 pt-0;
}
.title-field {
@apply bg-muted/50 text-sm font-medium py-1 table ml-[-14px] pl-4 pr-3 mb-3 mt-3;
align-self: flex-start;
}
}
.multischema {
@apply mt-3;
fieldset {
margin-top: 0 !important;
}
}
}
&.hide-required-mark {
.control-label span.required {
display: none;
}
}
.form-group {
@apply flex flex-col gap-1;
&:not(.field) {
@apply flex-grow;
}
/* hide empty description if markdown is enabled */
.field-description:has(> span:empty) {
display: none;
}
.control-label span.required {
@apply ml-1 opacity-50;
}
&.field.has-error {
@apply text-red-500;
.control-label {
@apply font-bold;
}
.error-detail:not(:only-child) {
@apply font-bold list-disc pl-6;
}
.error-detail:only-child {
@apply font-bold;
}
}
}
.field-description {
@apply text-primary/70 text-sm;
}
/* input but not radio */
input:not([type="radio"]):not([type="checkbox"]) {
@apply flex bg-muted/40 h-11 rounded-md outline-none;
@apply py-2.5 px-4;
width: 100%;
&:not([disabled]):not([readonly]) {
@apply focus:outline-none focus:ring-2 focus:bg-muted focus:ring-zinc-500 focus:border-transparent transition-all;
}
&[disabled],
&[readonly] {
@apply bg-muted/50 text-primary/50 cursor-not-allowed;
}
}
textarea {
@apply flex bg-muted/40 focus:bg-muted rounded-md outline-none focus:outline-none focus:ring-2 focus:ring-zinc-500 focus:border-transparent transition-all disabled:bg-muted/50 disabled:text-primary/50;
@apply py-2.5 px-4;
width: 100%;
}
.checkbox {
label,
label > span {
@apply flex flex-row gap-2;
}
}
select {
@apply bg-muted/40 focus:bg-muted rounded-md py-2.5 pr-4 pl-2.5 outline-none focus:outline-none focus:ring-2 focus:ring-zinc-500 focus:border-transparent transition-all;
@apply disabled:bg-muted/70 disabled:text-primary/70;
@apply w-full border-r-8 border-r-transparent;
&:not([multiple]) {
@apply h-11;
}
&[multiple] {
option {
@apply py-1.5 px-2.5 bg-transparent;
&:checked {
@apply bg-primary/20;
}
}
}
}
}
}
/* alternative */
&.fieldset-alternative {
fieldset {
@apply flex flex-grow flex-col gap-3.5;
&:has(> legend) {
@apply mt-3 border-l-4 border-solid border-muted/50 p-3 pb-0 pt-0;
.btn {
@apply w-5 h-5 bg-amber-500;
}
.field-radio-group {
@apply flex flex-row gap-2;
}
&.noborder-first-fieldset {
fieldset#root {
@apply border-none p-0;
}
}
&.horizontal {
.form-group {
@apply flex-row gap-2;
}
.form-control,
.panel {
@apply flex-grow;
}
.control-label {
@apply w-32 flex h-11 items-center;
}
input {
width: auto;
}
fieldset#root {
@apply gap-6;
}
fieldset.object-field {
@apply gap-2;
}
.additional-children {
.checkbox {
@apply w-full;
}
}
}
&.hide-multi-labels {
.control-label {
display: none;
}
}
.multischema {
.form-control {
@apply flex-shrink;
}
}
.panel {
/*@apply flex flex-col gap-2;*/
/*.control-label { display: none; }*/
& > .field-radio-group {
@apply flex flex-row gap-3;
.radio,
.radio-inline {
@apply text-sm border-b border-b-transparent;
@apply font-mono text-primary/70;
input {
@apply appearance-none;
}
.title-field {
@apply bg-muted/50 text-sm font-medium py-1 table ml-[-14px] pl-4 pr-3 mb-3 mt-3;
align-self: flex-start;
&.checked {
@apply border-b-primary/70 text-primary;
}
}
}
}
.multischema {
@apply mt-3;
fieldset {
margin-top: 0 !important;
}
}
}
&.hide-required-mark {
.control-label span.required {
display: none;
}
}
.form-group {
@apply flex flex-col gap-1;
&:not(.field) {
@apply flex-grow;
}
/* hide empty description if markdown is enabled */
.field-description:has(> span:empty) {
display: none;
}
.control-label span.required {
@apply ml-1 opacity-50;
}
&.field.has-error {
@apply text-red-500;
.control-label {
@apply font-bold;
}
.error-detail:not(:only-child) {
@apply font-bold list-disc pl-6;
}
.error-detail:only-child {
@apply font-bold;
}
}
}
.field-description {
@apply text-primary/70 text-sm;
}
/* input but not radio */
input:not([type="radio"]):not([type="checkbox"]) {
@apply flex bg-muted/40 h-11 rounded-md outline-none;
@apply py-2.5 px-4;
width: 100%;
&:not([disabled]):not([readonly]) {
@apply focus:outline-none focus:ring-2 focus:bg-muted focus:ring-zinc-500 focus:border-transparent transition-all;
}
&[disabled], &[readonly] {
@apply bg-muted/50 text-primary/50 cursor-not-allowed;
}
}
textarea {
@apply flex bg-muted/40 focus:bg-muted rounded-md outline-none focus:outline-none focus:ring-2 focus:ring-zinc-500 focus:border-transparent transition-all disabled:bg-muted/50 disabled:text-primary/50;
@apply py-2.5 px-4;
width: 100%;
}
.checkbox {
label, label > span {
@apply flex flex-row gap-2;
}
}
select {
@apply bg-muted/40 focus:bg-muted rounded-md py-2.5 pr-4 pl-2.5 outline-none focus:outline-none focus:ring-2 focus:ring-zinc-500 focus:border-transparent transition-all;
@apply disabled:bg-muted/70 disabled:text-primary/70;
@apply w-full border-r-8 border-r-transparent;
&:not([multiple]) {
@apply h-11;
}
&[multiple] {
option {
@apply py-1.5 px-2.5 bg-transparent;
&:checked {
@apply bg-primary/20;
}
}
}
}
.btn {
@apply w-5 h-5 bg-amber-500;
}
.field-radio-group {
@apply flex flex-row gap-2;
}
&.noborder-first-fieldset {
fieldset#root {
@apply border-none p-0;
}
}
&.horizontal {
.form-group {
@apply flex-row gap-2;
}
.form-control, .panel {
@apply flex-grow;
}
.control-label {
@apply w-32 flex h-11 items-center;
}
input {
width: auto;
}
fieldset#root {
@apply gap-6;
}
fieldset.object-field {
@apply gap-2;
}
.additional-children {
.checkbox {
@apply w-full;
}
}
}
&.hide-multi-labels {
.control-label {
display: none;
}
}
.multischema {
.form-control {
@apply flex-shrink;
}
}
.panel {
/*@apply flex flex-col gap-2;*/
/*.control-label { display: none; }*/
& > .field-radio-group {
@apply flex flex-row gap-3;
.radio, .radio-inline {
@apply text-sm border-b border-b-transparent;
@apply font-mono text-primary/70;
input {
@apply appearance-none;
}
&.checked {
@apply border-b-primary/70 text-primary;
}
}
}
/* :not(.panel-select) .control-label {
/* :not(.panel-select) .control-label {
display: none;
} */
.panel-select select {
@apply py-1 pr-1 pl-1.5 text-sm;
@apply h-auto w-auto;
}
}
.panel-select select {
@apply py-1 pr-1 pl-1.5 text-sm;
@apply h-auto w-auto;
}
}
&.legacy {
/* first fieldset */
& > .form-group.field-object > div > fieldset {
@apply border-none p-0;
}
&.legacy {
/* first fieldset */
& > .form-group.field-object>div>fieldset {
@apply border-none p-0;
}
.row {
display: flex;
flex-direction: row;
gap: 1rem;
}
.col-xs-5 {
display: flex;
width: 50%;
}
.form-additional {
fieldset {
/* padding: 0;
.row {
display: flex;
flex-direction: row;
gap: 1rem;
}
.col-xs-5 {
display: flex;
width: 50%;
}
.form-additional {
fieldset {
/* padding: 0;
border: none; */
/* legend {
/* legend {
display: none;
} */
}
&.additional-start {
> label {
display: none;
}
&.additional-start {
> label {
display: none;
}
/* > label + div > fieldset:first-child {
/* > label + div > fieldset:first-child {
display: none;
} */
}
}
.field-object + .field-object {
@apply mt-3 pt-4 border-t border-muted;
}
.panel>.field-object>label {
display: none;
}
}
}
}
}
.field-object + .field-object {
@apply mt-3 pt-4 border-t border-muted;
}
.panel > .field-object > label {
display: none;
}
}
}

View File

@@ -5,7 +5,7 @@ import {
type RJSFSchema,
type StrictRJSFSchema,
getTemplate,
getUiOptions
getUiOptions,
} from "@rjsf/utils";
import { cloneElement } from "react";
@@ -16,7 +16,7 @@ import { cloneElement } from "react";
export default function ArrayFieldTemplate<
T = any,
S extends StrictRJSFSchema = RJSFSchema,
F extends FormContextType = any
F extends FormContextType = any,
>(props: ArrayFieldTemplateProps<T, S, F>) {
const {
canAdd,
@@ -30,27 +30,27 @@ export default function ArrayFieldTemplate<
registry,
required,
schema,
title
title,
} = props;
const uiOptions = getUiOptions<T, S, F>(uiSchema);
const ArrayFieldDescriptionTemplate = getTemplate<"ArrayFieldDescriptionTemplate", T, S, F>(
"ArrayFieldDescriptionTemplate",
registry,
uiOptions
uiOptions,
);
const ArrayFieldItemTemplate = getTemplate<"ArrayFieldItemTemplate", T, S, F>(
"ArrayFieldItemTemplate",
registry,
uiOptions
uiOptions,
);
const ArrayFieldTitleTemplate = getTemplate<"ArrayFieldTitleTemplate", T, S, F>(
"ArrayFieldTitleTemplate",
registry,
uiOptions
uiOptions,
);
// Button templates are not overridden in the uiSchema
const {
ButtonTemplates: { AddButton }
ButtonTemplates: { AddButton },
} = registry.templates;
return (
<fieldset className={className} id={idSchema.$id}>
@@ -76,13 +76,13 @@ export default function ArrayFieldTemplate<
const newChildren = cloneElement(children, {
...children.props,
name: undefined,
title: undefined
title: undefined,
});
return (
<ArrayFieldItemTemplate key={key} {...itemProps} children={newChildren} />
);
}
},
)}
</div>
)}

View File

@@ -5,7 +5,7 @@ import {
type StrictRJSFSchema,
ariaDescribedByIds,
examplesId,
getInputProps
getInputProps,
} from "@rjsf/utils";
import { type ChangeEvent, type FocusEvent, useCallback } from "react";
import { Label } from "./FieldTemplate";
@@ -19,7 +19,7 @@ import { Label } from "./FieldTemplate";
export default function BaseInputTemplate<
T = any,
S extends StrictRJSFSchema = RJSFSchema,
F extends FormContextType = any
F extends FormContextType = any,
>(props: BaseInputTemplateProps<T, S, F>) {
const {
id,
@@ -52,7 +52,7 @@ export default function BaseInputTemplate<
}
const inputProps = {
...rest,
...getInputProps<T, S, F>(schema, type, options)
...getInputProps<T, S, F>(schema, type, options),
};
let inputValue;
@@ -65,15 +65,15 @@ export default function BaseInputTemplate<
const _onChange = useCallback(
({ target: { value } }: ChangeEvent<HTMLInputElement>) =>
onChange(value === "" ? options.emptyValue : value),
[onChange, options]
[onChange, options],
);
const _onBlur = useCallback(
({ target }: FocusEvent<HTMLInputElement>) => onBlur(id, target && target.value),
[onBlur, id]
[onBlur, id],
);
const _onFocus = useCallback(
({ target }: FocusEvent<HTMLInputElement>) => onFocus(id, target && target.value),
[onFocus, id]
[onFocus, id],
);
const shouldHideLabel =
@@ -108,7 +108,7 @@ export default function BaseInputTemplate<
.concat(
schema.default && !schema.examples.includes(schema.default)
? ([schema.default] as string[])
: []
: [],
)
.map((example: any) => {
return <option key={example} value={example} />;

View File

@@ -4,7 +4,7 @@ import {
type RJSFSchema,
type StrictRJSFSchema,
getTemplate,
getUiOptions
getUiOptions,
} from "@rjsf/utils";
import { identifierToHumanReadable } from "core/utils";
import { twMerge } from "tailwind-merge";
@@ -45,7 +45,7 @@ export function Label(props: LabelProps) {
export function FieldTemplate<
T = any,
S extends StrictRJSFSchema = RJSFSchema,
F extends FormContextType = any
F extends FormContextType = any,
>(props: FieldTemplateProps<T, S, F>) {
const {
id,
@@ -58,14 +58,14 @@ export function FieldTemplate<
required,
displayLabel,
registry,
uiSchema
uiSchema,
} = props;
const uiOptions = getUiOptions(uiSchema, registry.globalUiOptions);
//console.log("field---", uiOptions);
const WrapIfAdditionalTemplate = getTemplate<"WrapIfAdditionalTemplate", T, S, F>(
"WrapIfAdditionalTemplate",
registry,
uiOptions
uiOptions,
);
if (hidden) {
return <div className="hidden">{children}</div>;
@@ -81,7 +81,7 @@ export function FieldTemplate<
"flex flex-grow additional-children",
uiOptions.flexDirection === "row"
? "flex-row items-center gap-3"
: "flex-col flex-grow gap-2"
: "flex-col flex-grow gap-2",
)}
>
{children}

View File

@@ -8,7 +8,7 @@ import {
descriptionId,
getTemplate,
getUiOptions,
titleId
titleId,
} from "@rjsf/utils";
/** The `ObjectFieldTemplate` is the template to use to render all the inner properties of an object along with the
@@ -20,7 +20,7 @@ import {
export default function ObjectFieldTemplate<
T = any,
S extends StrictRJSFSchema = RJSFSchema,
F extends FormContextType = any
F extends FormContextType = any,
>(props: ObjectFieldTemplateProps<T, S, F>) {
const {
description,
@@ -34,18 +34,18 @@ export default function ObjectFieldTemplate<
required,
schema,
title,
uiSchema
uiSchema,
} = props;
const options = getUiOptions<T, S, F>(uiSchema);
const TitleFieldTemplate = getTemplate<"TitleFieldTemplate", T, S, F>(
"TitleFieldTemplate",
registry,
options
options,
);
const DescriptionFieldTemplate = getTemplate<"DescriptionFieldTemplate", T, S, F>(
"DescriptionFieldTemplate",
registry,
options
options,
);
/* if (properties.length === 0) {
@@ -59,7 +59,7 @@ export default function ObjectFieldTemplate<
// Button templates are not overridden in the uiSchema
const {
ButtonTemplates: { AddButton }
ButtonTemplates: { AddButton },
} = registry.templates;
return (

View File

@@ -10,7 +10,7 @@ const REQUIRED_FIELD_SYMBOL = "*";
export default function TitleField<
T = any,
S extends StrictRJSFSchema = RJSFSchema,
F extends FormContextType = any
F extends FormContextType = any,
>(props: TitleFieldProps<T, S, F>) {
const { id, title, required } = props;
return (

View File

@@ -4,7 +4,7 @@ import {
type RJSFSchema,
type StrictRJSFSchema,
TranslatableString,
type WrapIfAdditionalTemplateProps
type WrapIfAdditionalTemplateProps,
} from "@rjsf/utils";
import { useState } from "react";
@@ -16,7 +16,7 @@ import { useState } from "react";
export default function WrapIfAdditionalTemplate<
T = any,
S extends StrictRJSFSchema = RJSFSchema,
F extends FormContextType = any
F extends FormContextType = any,
>(props: WrapIfAdditionalTemplateProps<T, S, F>) {
const {
id,
@@ -31,7 +31,7 @@ export default function WrapIfAdditionalTemplate<
schema,
children,
uiSchema,
registry
registry,
} = props;
const { templates, translateString } = registry;
// Button templates are not overridden in the uiSchema

View File

@@ -15,5 +15,5 @@ export const templates = {
TitleFieldTemplate,
ObjectFieldTemplate,
BaseInputTemplate,
WrapIfAdditionalTemplate
WrapIfAdditionalTemplate,
};

View File

@@ -9,7 +9,7 @@ import type {
StrictRJSFSchema,
UiSchema,
ValidationData,
ValidatorType
ValidatorType,
} from "@rjsf/utils";
import { toErrorSchema } from "@rjsf/utils";
@@ -33,7 +33,7 @@ export class RJSFTypeboxValidator<T = any, S extends StrictRJSFSchema = RJSFSche
return {
errors: [...Errors(tbSchema, formData)],
validationError: null as any
validationError: null as any,
};
}
@@ -42,7 +42,7 @@ export class RJSFTypeboxValidator<T = any, S extends StrictRJSFSchema = RJSFSche
schema: S,
customValidate?: CustomValidator,
transformErrors?: ErrorTransformer,
uiSchema?: UiSchema
uiSchema?: UiSchema,
): ValidationData<T> {
const { errors } = this.rawValidation(schema, formData);
@@ -54,13 +54,13 @@ export class RJSFTypeboxValidator<T = any, S extends StrictRJSFSchema = RJSFSche
message: error.message,
property: "." + schemaLocation,
schemaPath: error.path,
stack: error.message
stack: error.message,
};
});
return {
errors: transformedErrors,
errorSchema: toErrorSchema(transformedErrors)
errorSchema: toErrorSchema(transformedErrors),
} as any;
}

View File

@@ -218,7 +218,7 @@ function FromConst<T extends SConst>(T: T) {
// ------------------------------------------------------------------
type TFromPropertiesIsOptional<
K extends PropertyKey,
R extends string | unknown
R extends string | unknown,
> = unknown extends R ? true : K extends R ? false : true;
// prettier-ignore
// biome-ignore format:
@@ -243,13 +243,13 @@ function FromObject<T extends SObject>(T: T): TFromObject<T> {
// biome-ignore lint: reason
T.required && T.required.includes(K)
? FromSchema(T.properties[K])
: Type.Optional(FromSchema(T.properties[K]))
: Type.Optional(FromSchema(T.properties[K])),
};
}, {} as Type.TProperties);
if ("additionalProperties" in T) {
return Type.Object(properties, {
additionalProperties: FromSchema(T.additionalProperties)
additionalProperties: FromSchema(T.additionalProperties),
}) as never;
}

View File

@@ -5,7 +5,7 @@ import { type ChangeEvent, useCallback } from "react";
export function CheckboxWidget<
T = any,
S extends StrictRJSFSchema = RJSFSchema,
F extends FormContextType = any
F extends FormContextType = any,
>({
schema,
uiSchema,
@@ -33,7 +33,7 @@ export function CheckboxWidget<
});*/
const handleChange = useCallback(
(event: ChangeEvent<HTMLInputElement>) => onChange(event.target.checked),
[onChange]
[onChange],
);
return (

View File

@@ -8,7 +8,7 @@ import {
enumOptionsIsSelected,
enumOptionsSelectValue,
enumOptionsValueForIndex,
optionId
optionId,
} from "@rjsf/utils";
import { type ChangeEvent, type FocusEvent, useCallback } from "react";
@@ -20,7 +20,7 @@ import { type ChangeEvent, type FocusEvent, useCallback } from "react";
function CheckboxesWidget<
T = any,
S extends StrictRJSFSchema = RJSFSchema,
F extends FormContextType = any
F extends FormContextType = any,
>({
id,
disabled,
@@ -30,20 +30,20 @@ function CheckboxesWidget<
readonly,
onChange,
onBlur,
onFocus
onFocus,
}: WidgetProps<T, S, F>) {
const checkboxesValues = Array.isArray(value) ? value : [value];
const handleBlur = useCallback(
({ target }: FocusEvent<HTMLInputElement>) =>
onBlur(id, enumOptionsValueForIndex<S>(target?.value, enumOptions, emptyValue)),
[onBlur, id]
[onBlur, id],
);
const handleFocus = useCallback(
({ target }: FocusEvent<HTMLInputElement>) =>
onFocus(id, enumOptionsValueForIndex<S>(target?.value, enumOptions, emptyValue)),
[onFocus, id]
[onFocus, id],
);
return (
<div className="checkboxes" id={id}>

View File

@@ -6,7 +6,7 @@ import {
ariaDescribedByIds,
enumOptionsIsSelected,
enumOptionsValueForIndex,
optionId
optionId,
} from "@rjsf/utils";
import { type FocusEvent, useCallback } from "react";
@@ -18,7 +18,7 @@ import { type FocusEvent, useCallback } from "react";
function RadioWidget<
T = any,
S extends StrictRJSFSchema = RJSFSchema,
F extends FormContextType = any
F extends FormContextType = any,
>({
options,
value,
@@ -29,20 +29,20 @@ function RadioWidget<
onBlur,
onFocus,
onChange,
id
id,
}: WidgetProps<T, S, F>) {
const { enumOptions, enumDisabled, inline, emptyValue } = options;
const handleBlur = useCallback(
({ target: { value } }: FocusEvent<HTMLInputElement>) =>
onBlur(id, enumOptionsValueForIndex<S>(value, enumOptions, emptyValue)),
[onBlur, id]
[onBlur, id],
);
const handleFocus = useCallback(
({ target: { value } }: FocusEvent<HTMLInputElement>) =>
onFocus(id, enumOptionsValueForIndex<S>(value, enumOptions, emptyValue)),
[onFocus, id]
[onFocus, id],
);
return (

View File

@@ -5,7 +5,7 @@ import {
type WidgetProps,
ariaDescribedByIds,
enumOptionsIndexForValue,
enumOptionsValueForIndex
enumOptionsValueForIndex,
} from "@rjsf/utils";
import { type ChangeEvent, type FocusEvent, type SyntheticEvent, useCallback } from "react";
@@ -27,7 +27,7 @@ function getValue(event: SyntheticEvent<HTMLSelectElement>, multiple: boolean) {
function SelectWidget<
T = any,
S extends StrictRJSFSchema = RJSFSchema,
F extends FormContextType = any
F extends FormContextType = any,
>({
schema,
id,
@@ -41,7 +41,7 @@ function SelectWidget<
onChange,
onBlur,
onFocus,
placeholder
placeholder,
}: WidgetProps<T, S, F>) {
const { enumOptions, enumDisabled, emptyValue: optEmptyVal } = options;
const emptyValue = multiple ? [] : "";
@@ -51,7 +51,7 @@ function SelectWidget<
const newValue = getValue(event, multiple);
return onFocus(id, enumOptionsValueForIndex<S>(newValue, enumOptions, optEmptyVal));
},
[onFocus, id, schema, multiple, enumOptions, optEmptyVal]
[onFocus, id, schema, multiple, enumOptions, optEmptyVal],
);
const handleBlur = useCallback(
@@ -59,7 +59,7 @@ function SelectWidget<
const newValue = getValue(event, multiple);
return onBlur(id, enumOptionsValueForIndex<S>(newValue, enumOptions, optEmptyVal));
},
[onBlur, id, schema, multiple, enumOptions, optEmptyVal]
[onBlur, id, schema, multiple, enumOptions, optEmptyVal],
);
const handleChange = useCallback(
@@ -67,7 +67,7 @@ function SelectWidget<
const newValue = getValue(event, multiple);
return onChange(enumOptionsValueForIndex<S>(newValue, enumOptions, optEmptyVal));
},
[onChange, schema, multiple, enumOptions, optEmptyVal]
[onChange, schema, multiple, enumOptions, optEmptyVal],
);
const selectedIndexes = enumOptionsIndexForValue<S>(value, enumOptions, multiple);

View File

@@ -26,5 +26,5 @@ export const widgets = {
CheckboxWidget: WithLabel(CheckboxWidget),
SelectWidget: WithLabel(SelectWidget, "select"),
CheckboxesWidget: WithLabel(CheckboxesWidget),
JsonWidget: WithLabel(JsonWidget)
JsonWidget: WithLabel(JsonWidget),
};

View File

@@ -4,7 +4,7 @@ import {
type FormEvent,
useEffect,
useRef,
useState
useState,
} from "react";
import { useEvent } from "ui/hooks/use-event";
import {
@@ -14,7 +14,7 @@ import {
coerce,
getFormTarget,
getTargetsByName,
setPath
setPath,
} from "./utils";
export type NativeFormProps = {
@@ -25,12 +25,12 @@ export type NativeFormProps = {
onSubmit?: (data: any, ctx: { event: FormEvent<HTMLFormElement> }) => Promise<void> | void;
onSubmitInvalid?: (
errors: InputError[],
ctx: { event: FormEvent<HTMLFormElement> }
ctx: { event: FormEvent<HTMLFormElement> },
) => Promise<void> | void;
onError?: (errors: InputError[]) => void;
onChange?: (
data: any,
ctx: { event: ChangeEvent<HTMLFormElement>; key: string; value: any; errors: InputError[] }
ctx: { event: ChangeEvent<HTMLFormElement>; key: string; value: any; errors: InputError[] },
) => Promise<void> | void;
clean?: CleanOptions | true;
} & Omit<ComponentPropsWithoutRef<"form">, "onChange" | "onSubmit">;
@@ -77,13 +77,13 @@ export function NativeForm({
const validateElement = useEvent((el: InputElement | null, opts?: { report?: boolean }) => {
if (props.noValidate || !el || !("name" in el)) return;
const errorElement = formRef.current?.querySelector(
errorFieldSelector?.(el.name) ?? `[data-role="input-error"][data-name="${el.name}"]`
errorFieldSelector?.(el.name) ?? `[data-role="input-error"][data-name="${el.name}"]`,
);
if (!el.checkValidity()) {
const error = {
name: el.name,
message: el.validationMessage
message: el.validationMessage,
};
setErrors((prev) => [...prev.filter((e) => e.name !== el.name), error]);
@@ -165,7 +165,7 @@ export function NativeForm({
event: e,
key: target.name,
value: target.value,
errors
errors,
});
}
});

View File

@@ -70,14 +70,14 @@ export type CleanOptions = {
};
export function cleanObject<Obj extends { [key: string]: any }>(
obj: Obj,
_opts?: CleanOptions
_opts?: CleanOptions,
): Obj {
if (!obj) return obj;
const _empty = [null, undefined, ""];
const opts = {
empty: _opts?.empty ?? _empty,
emptyInArray: _opts?.emptyInArray ?? _empty,
keepEmptyArray: _opts?.keepEmptyArray ?? false
keepEmptyArray: _opts?.keepEmptyArray ?? false,
};
return Object.entries(obj).reduce((acc, [key, value]) => {

View File

@@ -5,7 +5,7 @@ import {
type DraggableRubric,
type DraggableStateSnapshot,
Droppable,
type DroppableProps
type DroppableProps,
} from "@hello-pangea/dnd";
import type { ElementProps } from "@mantine/core";
import { useListState } from "@mantine/hooks";

View File

@@ -21,13 +21,13 @@ export type Modal2Props = Omit<ModalProps, "opened" | "onClose"> & {
export const Modal2 = forwardRef<Modal2Ref, Modal2Props>(
(
{ classNames, children, opened: initialOpened, closeOnClickOutside = false, ...props },
ref
ref,
) => {
const [opened, { open, close }] = useDisclosure(initialOpened);
useImperativeHandle(ref, () => ({
open,
close
close,
}));
return (
@@ -42,13 +42,13 @@ export const Modal2 = forwardRef<Modal2Ref, Modal2Props>(
classNames={{
...classNames,
root: "bknd-admin",
content: "rounded-lg select-none"
content: "rounded-lg select-none",
}}
>
{children}
</Modal>
);
}
},
);
export const ModalTitle = ({ path, onClose }: { path: string[]; onClose: () => void }) => {
@@ -76,7 +76,7 @@ export const ModalBody = ({ children, className }: { children?: any; className?:
<ScrollArea.Root
className={twMerge("flex flex-col min-h-96", className)}
style={{
maxHeight: "calc(80vh)"
maxHeight: "calc(80vh)",
}}
>
<ScrollArea.Viewport className="w-full h-full">
@@ -104,7 +104,7 @@ export const ModalFooter = ({
prev,
nextLabel = "Next",
prevLabel = "Back",
debug
debug,
}: {
next: any;
prev: any;
@@ -123,7 +123,7 @@ export const ModalFooter = ({
shadow="md"
opened={opened}
classNames={{
dropdown: "!px-1 !pr-2.5 !py-2 text-sm"
dropdown: "!px-1 !pr-2.5 !py-2 text-sm",
}}
>
<Popover.Target>

View File

@@ -5,7 +5,7 @@ import {
Fragment,
type ReactElement,
cloneElement,
useState
useState,
} from "react";
import { twMerge } from "tailwind-merge";
import { useEvent } from "ui/hooks/use-event";
@@ -36,7 +36,7 @@ export type DropdownProps = {
onClickItem?: (item: DropdownItem) => void;
renderItem?: (
item: DropdownItem,
props: { key: number; onClick: () => void }
props: { key: number; onClick: () => void },
) => DropdownClickableChild;
};
@@ -52,7 +52,7 @@ export function Dropdown({
onClickItem,
renderItem,
itemsClassName,
className
className,
}: DropdownProps) {
const [open, setOpen] = useState(defaultOpen);
const [position, setPosition] = useState(initialPosition);
@@ -61,7 +61,7 @@ export function Dropdown({
const [_offset, _setOffset] = useState(0);
const toggle = useEvent((delay: number = 50) =>
setTimeout(() => setOpen((prev) => !prev), typeof delay === "number" ? delay : 0)
setTimeout(() => setOpen((prev) => !prev), typeof delay === "number" ? delay : 0),
);
const onClickHandler = openEvent === "onClick" ? toggle : undefined;
@@ -106,7 +106,7 @@ export function Dropdown({
"bottom-start": { top: "100%", left: _offset, marginTop: offset },
"bottom-end": { right: _offset, top: "100%", marginTop: offset },
"top-start": { bottom: "100%", marginBottom: offset },
"top-end": { bottom: "100%", right: _offset, marginBottom: offset }
"top-end": { bottom: "100%", right: _offset, marginBottom: offset },
}[position];
const internalOnClickItem = useEvent((item) => {
@@ -132,7 +132,7 @@ export function Dropdown({
"flex flex-row flex-nowrap text-nowrap items-center outline-none cursor-pointer px-2.5 rounded-md link leading-none h-8",
itemsClassName,
item.disabled ? "opacity-50 cursor-not-allowed" : "hover:bg-primary/10",
item.destructive && "text-red-500 hover:bg-red-600 hover:text-white"
item.destructive && "text-red-500 hover:bg-red-600 hover:text-white",
)}
onClick={onClick}
>
@@ -159,7 +159,7 @@ export function Dropdown({
{...dropdownWrapperProps}
className={twMerge(
"absolute z-30 flex flex-col bg-background border border-muted px-1 py-1 rounded-lg shadow-lg min-w-full",
dropdownWrapperProps?.className
dropdownWrapperProps?.className,
)}
style={dropdownStyle}
>
@@ -167,7 +167,7 @@ export function Dropdown({
<div className="text-sm font-bold px-2.5 mb-1 mt-1 opacity-50">{title}</div>
)}
{menuItems.map((item, i) =>
itemRenderer(item, { key: i, onClick: () => internalOnClickItem(item) })
itemRenderer(item, { key: i, onClick: () => internalOnClickItem(item) }),
)}
</div>
)}

View File

@@ -17,7 +17,7 @@ export function Modal({
onClose = () => null,
allowBackdropClose = true,
className,
stickToTop
stickToTop,
}: ModalProps) {
const clickoutsideRef = useClickOutside(() => {
if (allowBackdropClose) onClose();
@@ -29,13 +29,13 @@ export function Modal({
<div
className={twMerge(
"w-full h-full fixed bottom-0 top-0 right-0 left-0 bg-background/60 flex justify-center backdrop-blur-sm z-10",
stickToTop ? "items-start" : "items-center"
stickToTop ? "items-start" : "items-center",
)}
>
<div
className={twMerge(
"z-20 flex flex-col bg-background rounded-lg shadow-lg",
className
className,
)}
ref={clickoutsideRef}
>

View File

@@ -20,20 +20,20 @@ export function Popover({
backdrop = false,
position = "bottom-start",
overlayProps,
className
className,
}: PopoverProps) {
const [open, setOpen] = useState(defaultOpen);
const clickoutsideRef = useClickOutside(() => setOpen(false));
const toggle = useEvent((delay: number = 50) =>
setTimeout(() => setOpen((prev) => !prev), typeof delay === "number" ? delay : 0)
setTimeout(() => setOpen((prev) => !prev), typeof delay === "number" ? delay : 0),
);
const pos = {
"bottom-start": "mt-1 top-[100%]",
"bottom-end": "right-0 top-[100%] mt-1",
"top-start": "bottom-[100%] mb-1",
"top-end": "bottom-[100%] right-0 mb-1"
"top-end": "bottom-[100%] right-0 mb-1",
}[position];
return (
@@ -49,7 +49,7 @@ export function Popover({
className={twMerge(
"animate-fade-in absolute z-20 flex flex-col bg-background border border-muted px-1 py-1 rounded-lg shadow-lg backdrop-blur-sm min-w-full max-w-20",
pos,
overlayProps?.className
overlayProps?.className,
)}
>
{target({ toggle })}

View File

@@ -4,7 +4,7 @@ import {
type SetStateAction,
createContext,
useContext,
useState
useState,
} from "react";
export type TStepsProps = {
@@ -30,7 +30,7 @@ export function Steps({ children, initialPath = [], initialState = {}, lastBack
const [state, setState] = useState<any>(initialState);
const [path, setPath] = useState<string[]>(initialPath);
const steps: any[] = Children.toArray(children).filter(
(child: any) => child.props.disabled !== true
(child: any) => child.props.disabled !== true,
);
function stepBack() {

View File

@@ -11,7 +11,7 @@ import {
TbDotsVertical,
TbSelector,
TbSquare,
TbSquareCheckFilled
TbSquareCheckFilled,
} from "react-icons/tb";
import { twMerge } from "tailwind-merge";
import { Button } from "ui/components/buttons/Button";
@@ -69,7 +69,7 @@ export function DataTable<Data extends Record<string, any> = Record<string, any>
renderHeader,
rowActions,
renderValue,
onClickNew
onClickNew,
}: DataTableProps<Data>) {
total = total || data?.length || 0;
page = page || 1;
@@ -105,7 +105,7 @@ export function DataTable<Data extends Record<string, any> = Record<string, any>
type="button"
className={twMerge(
"link hover:bg-primary/5 py-1.5 rounded-md inline-flex flex-row justify-start items-center gap-1",
onClickSort ? "pl-2.5 pr-1" : "px-2.5"
onClickSort ? "pl-2.5 pr-1" : "px-2.5",
)}
onClick={() => onClickSort?.(property)}
>
@@ -210,7 +210,7 @@ export function DataTable<Data extends Record<string, any> = Record<string, any>
<Dropdown
items={perPageOptions.map((perPage) => ({
label: String(perPage),
perPage
perPage,
}))}
position="top-end"
onClickItem={(item: any) => onClickPerPage?.(item.perPage)}
@@ -254,7 +254,7 @@ export const CellValue = ({ value, property }) => {
const SortIndicator = ({
sort,
field
field,
}: {
sort: Pick<DataTableProps<any>, "sort">["sort"];
field: string;
@@ -292,7 +292,7 @@ const TableNav: React.FC<TableNavProps> = ({ current, total, onClick }: TableNav
{ value: 1, Icon: TbChevronsLeft, disabled: current === 1 },
{ value: current - 1, Icon: TbChevronLeft, disabled: current === 1 },
{ value: current + 1, Icon: TbChevronRight, disabled: current === total },
{ value: total, Icon: TbChevronsRight, disabled: current === total }
{ value: total, Icon: TbChevronsRight, disabled: current === total },
] as const;
return navMap.map((nav, key) => (

View File

@@ -38,7 +38,7 @@ const useLocationFromRouter = (router) => {
// (This is achieved via `useEvent`.)
return [
unescape(relativePath(router.base, location)),
useEvent((to, navOpts) => navigate(absolutePath(to, router.base), navOpts))
useEvent((to, navOpts) => navigate(absolutePath(to, router.base), navOpts)),
];
};
@@ -64,7 +64,7 @@ export function Link({
const href = router
.hrefs(
_href[0] === "~" ? _href.slice(1) : router.base + _href,
router // pass router as a second argument for convinience
router, // pass router as a second argument for convinience
)
.replace("//", "/");
const absPath = absolutePath(path, router.base).replace("//", "/");

View File

@@ -6,7 +6,7 @@ export function useFlows() {
return {
flows: app.flows,
config: app.config.flows
config: app.config.flows,
};
}
@@ -17,6 +17,6 @@ export function useFlow(name: string) {
return {
flow: flow!,
config: app.config.flows[name]
config: app.config.flows[name],
};
}

View File

@@ -30,11 +30,11 @@ export type LoginFormProps = Omit<ComponentPropsWithoutRef<"form">, "onSubmit" |
const validator = new TypeboxValidator();
const schema = Type.Object({
email: Type.String({
pattern: "^[\\w-\\.]+@([\\w-]+\\.)+[\\w-]{2,4}$"
pattern: "^[\\w-\\.]+@([\\w-]+\\.)+[\\w-]{2,4}$",
}),
password: Type.String({
minLength: 8 // @todo: this should be configurable
})
minLength: 8, // @todo: this should be configurable
}),
});
export function AuthForm({
@@ -49,7 +49,7 @@ export function AuthForm({
const basepath = auth?.basepath ?? "/api/auth";
const password = {
action: `${basepath}/password/${action}`,
strategy: auth?.strategies?.password ?? ({ type: "password" } as const)
strategy: auth?.strategies?.password ?? ({ type: "password" } as const),
};
const oauth = transform(
@@ -59,7 +59,7 @@ export function AuthForm({
result[key] = value.config;
}
},
{}
{},
) as Record<string, AppAuthOAuthStrategy>;
const has_oauth = Object.keys(oauth).length > 0;

View File

@@ -15,7 +15,7 @@ export function AuthScreen({
action = "login",
logo,
intro,
formOnly
formOnly,
}: AuthScreenProps) {
const { strategies, basepath, loading } = useAuthStrategies();
const Form = <AuthForm auth={{ basepath, strategies }} method={method} action={action} />;

View File

@@ -20,7 +20,7 @@ export function SocialLink({
action,
method = "POST",
basepath = "/api/auth",
children
children,
}: SocialLinkProps) {
const url = [basepath, provider, action].join("/");

View File

@@ -6,7 +6,7 @@ import { SocialLink } from "./SocialLink";
export const Auth = {
Screen: AuthScreen,
Form: AuthForm,
SocialLink: SocialLink
SocialLink: SocialLink,
};
export { useAuthStrategies };

View File

@@ -6,7 +6,7 @@ import {
memo,
useEffect,
useRef,
useState
useState,
} from "react";
import { TbDots } from "react-icons/tb";
import { twMerge } from "tailwind-merge";
@@ -84,7 +84,7 @@ export function Dropzone({
onRejected,
onDeleted,
onUploaded,
children
children,
}: DropzoneProps) {
const [files, setFiles] = useState<FileState[]>(initialItems);
const [uploading, setUploading] = useState<boolean>(false);
@@ -116,7 +116,7 @@ export function Dropzone({
const specs = items.map((item) => ({
kind: "kind" in item ? item.kind : "file",
type: item.type,
size: "size" in item ? item.size : 0
size: "size" in item ? item.size : 0,
}));
return specs.every((spec) => {
@@ -164,7 +164,7 @@ export function Dropzone({
size: f.size,
type: f.type,
state: "pending",
progress: 0
progress: 0,
}));
return flow === "start" ? [...filteredFiles, ..._prev] : [..._prev, ...filteredFiles];
@@ -185,7 +185,7 @@ export function Dropzone({
},
onLeave: () => {
setIsOverAccepted(false);
}
},
});
useEffect(() => {
@@ -223,11 +223,11 @@ export function Dropzone({
return {
...f,
state,
progress: progress ?? f.progress
progress: progress ?? f.progress,
};
}
return f;
})
}),
);
}
@@ -237,11 +237,11 @@ export function Dropzone({
if (f.path === prevPath) {
return {
...f,
...newState
...newState,
};
}
return f;
})
}),
);
}
@@ -293,7 +293,7 @@ export function Dropzone({
console.log(`Progress: ${percentComplete.toFixed(2)}%`);
} else {
console.log(
"Unable to compute progress information since the total size is unknown"
"Unable to compute progress information since the total size is unknown",
);
}
});
@@ -311,7 +311,7 @@ export function Dropzone({
const newState = {
...response.state,
progress: 1,
state: "uploaded"
state: "uploaded",
};
replaceFileState(file.path, newState);
@@ -364,7 +364,7 @@ export function Dropzone({
const openFileInput = () => inputRef.current?.click();
const showPlaceholder = Boolean(
placeholder?.show === true || !maxItems || (maxItems && files.length < maxItems)
placeholder?.show === true || !maxItems || (maxItems && files.length < maxItems),
);
const renderProps: DropzoneRenderProps = {
@@ -373,25 +373,25 @@ export function Dropzone({
ref: inputRef,
type: "file",
multiple: !maxItems || maxItems > 1,
onChange: handleFileInputChange
onChange: handleFileInputChange,
},
state: {
files,
isOver,
isOverAccepted,
showPlaceholder
showPlaceholder,
},
actions: {
uploadFile,
deleteFile,
openFileInput
openFileInput,
},
dropzoneProps: {
maxItems,
placeholder,
autoUpload,
flow
}
flow,
},
};
return children ? children(renderProps) : <DropzoneInner {...renderProps} />;
@@ -402,7 +402,7 @@ const DropzoneInner = ({
inputProps,
state: { files, isOver, isOverAccepted, showPlaceholder },
actions: { uploadFile, deleteFile, openFileInput },
dropzoneProps: { placeholder, flow }
dropzoneProps: { placeholder, flow },
}: DropzoneRenderProps) => {
const Placeholder = showPlaceholder && (
<UploadPlaceholder onClick={openFileInput} text={placeholder?.text} />
@@ -422,7 +422,7 @@ const DropzoneInner = ({
className={twMerge(
"dropzone w-full h-full align-start flex flex-col select-none",
isOver && isOverAccepted && "bg-green-200/10",
isOver && !isOverAccepted && "bg-red-200/40 cursor-not-allowed"
isOver && !isOverAccepted && "bg-red-200/40 cursor-not-allowed",
)}
>
<div className="hidden">
@@ -478,7 +478,7 @@ const Wrapper = ({ file, fallback, ...props }: PreviewComponentProps) => {
};
export const PreviewWrapperMemoized = memo(
Wrapper,
(prev, next) => prev.file.path === next.file.path
(prev, next) => prev.file.path === next.file.path,
);
type PreviewProps = {
@@ -490,12 +490,12 @@ const Preview: React.FC<PreviewProps> = ({ file, handleUpload, handleDelete }) =
const dropdownItems = [
["initial", "uploaded"].includes(file.state) && {
label: "Delete",
onClick: () => handleDelete(file)
onClick: () => handleDelete(file),
},
["initial", "pending"].includes(file.state) && {
label: "Upload",
onClick: () => handleUpload(file)
}
onClick: () => handleUpload(file),
},
];
return (
@@ -503,7 +503,7 @@ const Preview: React.FC<PreviewProps> = ({ file, handleUpload, handleDelete }) =
className={twMerge(
"w-[49%] md:w-60 flex flex-col border border-muted relative",
file.state === "failed" && "border-red-500 bg-red-200/20",
file.state === "deleting" && "opacity-70"
file.state === "deleting" && "opacity-70",
)}
>
<div className="absolute top-2 right-2">

View File

@@ -38,7 +38,7 @@ export function DropzoneContainer({
const baseUrl = api.baseUrl;
const defaultQuery = {
limit: query?.limit ? query?.limit : props.maxItems ? props.maxItems : 50,
sort: "-id"
sort: "-id",
};
const entity_name = (media?.entity_name ?? "media") as "media";
//console.log("dropzone:baseUrl", baseUrl);
@@ -51,12 +51,12 @@ export function DropzoneContainer({
where: {
reference: `${entity.name}.${entity.field}`,
entity_id: entity.id,
...query?.where
}
...query?.where,
},
})
: api.data.readMany(entity_name, {
...defaultQuery,
...query
...query,
});
const $q = useApiQuery(selectApi, { enabled: initialItems !== false && !initialItems });
@@ -69,7 +69,7 @@ export function DropzoneContainer({
return {
url,
headers: api.media.getUploadHeaders(),
method: "POST"
method: "POST",
};
});

View File

@@ -9,7 +9,7 @@ import { guess } from "media/storage/mime-types-tiny";
const FILES_TO_IGNORE = [
// Thumbnail cache files for macOS and Windows
".DS_Store", // macOs
"Thumbs.db" // Windows
"Thumbs.db", // Windows
];
export function toFileWithPath(file: FileWithPath, path?: string): FileWithPath {
@@ -29,7 +29,7 @@ export function toFileWithPath(file: FileWithPath, path?: string): FileWithPath
: file.name,
writable: false,
configurable: false,
enumerable: true
enumerable: true,
});
}
@@ -55,7 +55,7 @@ function withMimeType(file: FileWithPath) {
value: type,
writable: false,
configurable: false,
enumerable: true
enumerable: true,
});
}
}
@@ -105,7 +105,7 @@ function isObject<T>(v: any): v is T {
function getInputFiles(evt: Event) {
return fromList<FileWithPath>((evt.target as HTMLInputElement).files).map((file) =>
toFileWithPath(file)
toFileWithPath(file),
);
}
@@ -179,9 +179,9 @@ function flatten<T>(items: any[]): T[] {
(acc, files) => [
// biome-ignore lint/performance/noAccumulatingSpread: <explanation>
...acc,
...(Array.isArray(files) ? flatten(files) : [files])
...(Array.isArray(files) ? flatten(files) : [files]),
],
[]
[],
);
}
@@ -229,7 +229,7 @@ function fromDirEntry(entry: any) {
},
(err: any) => {
reject(err);
}
},
);
}
@@ -247,7 +247,7 @@ async function fromFileEntry(entry: any) {
},
(err: any) => {
reject(err);
}
},
);
});
}

View File

@@ -6,7 +6,7 @@ export function mediaItemToFileState(
options: {
overrides?: Partial<FileState>;
baseUrl?: string;
} = { overrides: {}, baseUrl: "" }
} = { overrides: {}, baseUrl: "" },
): FileState {
return {
body: `${options.baseUrl}/api/media/file/${item.path}`,
@@ -16,7 +16,7 @@ export function mediaItemToFileState(
type: item.mime_type ?? "",
state: "uploaded",
progress: 0,
...options.overrides
...options.overrides,
};
}
@@ -25,7 +25,7 @@ export function mediaItemsToFileStates(
options: {
overrides?: Partial<FileState>;
baseUrl?: string;
} = { overrides: {}, baseUrl: "" }
} = { overrides: {}, baseUrl: "" },
): FileState[] {
return items.map((item) => mediaItemToFileState(item, options));
}

View File

@@ -4,7 +4,7 @@ import { DropzoneContainer, useDropzone } from "./DropzoneContainer";
export const Media = {
Dropzone: DropzoneContainer,
Preview: PreviewWrapperMemoized,
useDropzone: useDropzone
useDropzone: useDropzone,
};
export { useDropzone as useMediaDropzone };
@@ -14,6 +14,6 @@ export type {
FileState,
FileStateWithData,
DropzoneProps,
DropzoneRenderProps
DropzoneRenderProps,
} from "./Dropzone";
export type { DropzoneContainerProps } from "./DropzoneContainer";

View File

@@ -9,7 +9,7 @@ type DropzoneProps = {
const events = {
enter: ["dragenter", "dragover", "dragstart"] as const,
leave: ["dragleave", "drop"] as const
leave: ["dragleave", "drop"] as const,
};
const allEvents = [...events.enter, ...events.leave];
@@ -45,7 +45,7 @@ export function useDropzone({ onDropped, onOver, onLeave }: DropzoneProps) {
onDropped?.(files as any);
onOverCalled.current = false;
},
[onDropped]
[onDropped],
);
const handleFileInputChange = useCallback(
@@ -53,7 +53,7 @@ export function useDropzone({ onDropped, onOver, onLeave }: DropzoneProps) {
const files = await fromEvent(e);
onDropped?.(files as any);
},
[onDropped]
[onDropped],
);
useEffect(() => {

View File

@@ -5,7 +5,7 @@ import {
Type,
decodeSearch,
encodeSearch,
parseDecode
parseDecode,
} from "core/utils";
import { isEqual, transform } from "lodash-es";
import { useLocation, useSearch as useWouterSearch } from "wouter";
@@ -13,7 +13,7 @@ import { useLocation, useSearch as useWouterSearch } from "wouter";
// @todo: migrate to Typebox
export function useSearch<Schema extends TSchema = TSchema>(
schema: Schema,
defaultValue?: Partial<StaticDecode<Schema>>
defaultValue?: Partial<StaticDecode<Schema>>,
) {
const searchString = useWouterSearch();
const [location, navigate] = useLocation();
@@ -34,7 +34,7 @@ export function useSearch<Schema extends TSchema = TSchema>(
if (defaultValue && isEqual(value, defaultValue[key])) return;
result[key] = value;
},
{} as Static<Schema>
{} as Static<Schema>,
);
const encoded = encodeSearch(search, { encode: false });
navigate(location + (encoded.length > 0 ? "?" + encoded : ""));
@@ -42,6 +42,6 @@ export function useSearch<Schema extends TSchema = TSchema>(
return {
value: value as Required<StaticDecode<Schema>>,
set
set,
};
}

View File

@@ -7,7 +7,7 @@ import {
type ComponentPropsWithoutRef,
useEffect,
useRef,
useState
useState,
} from "react";
import type { IconType } from "react-icons";
import { twMerge } from "tailwind-merge";
@@ -50,7 +50,7 @@ export const NavLink = <E extends React.ElementType = "a">({
className={twMerge(
"px-6 py-2 [&.active]:bg-muted [&.active]:hover:bg-primary/15 hover:bg-primary/5 flex flex-row items-center rounded-full gap-2.5 link",
disabled && "opacity-50 cursor-not-allowed",
className
className,
)}
>
{Icon && <Icon size={18} />}
@@ -65,7 +65,7 @@ export function Content({ children, center }: { children: React.ReactNode; cente
data-shell="content"
className={twMerge(
"flex flex-1 flex-row w-dvw h-full",
center && "justify-center items-center"
center && "justify-center items-center",
)}
>
{children}
@@ -80,7 +80,7 @@ export function Main({ children }) {
data-shell="main"
className={twMerge(
"flex flex-col flex-grow w-1 flex-shrink-1",
sidebar.open && "max-w-[calc(100%-350px)]"
sidebar.open && "max-w-[calc(100%-350px)]",
)}
>
{children}
@@ -155,13 +155,13 @@ export function SectionHeader({ children, right, className, scrollable, sticky }
className={twMerge(
"flex flex-row h-14 flex-shrink-0 py-2 pl-5 pr-3 border-muted border-b items-center justify-between bg-muted/10",
sticky && "sticky top-0 bottom-10 z-10",
className
className,
)}
>
<div
className={twMerge(
"",
scrollable && "overflow-x-scroll overflow-y-visible app-scrollbar"
scrollable && "overflow-x-scroll overflow-y-visible app-scrollbar",
)}
>
{typeof children === "string" ? (
@@ -206,7 +206,7 @@ export const SidebarLink = <E extends React.ElementType = "a">({
!disabled &&
"cursor-pointer rounded-md [&.active]:bg-primary/10 [&.active]:hover:bg-primary/15 [&.active]:font-medium hover:bg-primary/5 focus:bg-primary/5 link",
disabled && "opacity-50 cursor-not-allowed pointer-events-none",
className
className,
)}
>
{children}
@@ -243,7 +243,7 @@ export const SectionHeaderLink = <E extends React.ElementType = "a">({
? "bg-background hover:bg-background text-primary border border-muted border-b-0"
: "link",
badge && "pr-4",
className
className,
)}
>
{children}
@@ -283,7 +283,7 @@ export const SectionHeaderTabs = ({ title, items }: SectionHeaderTabsProps) => {
export function Scrollable({
children,
initialOffset = 64
initialOffset = 64,
}: {
children: React.ReactNode;
initialOffset?: number;
@@ -330,7 +330,7 @@ export const SectionHeaderAccordionItem = ({
toggle,
ActiveIcon = IconChevronUp,
children,
renderHeaderRight
renderHeaderRight,
}: {
title: string;
open: boolean;
@@ -345,12 +345,12 @@ export const SectionHeaderAccordionItem = ({
"flex flex-col flex-animate overflow-hidden",
open
? "flex-open border-b border-b-muted"
: "flex-initial cursor-pointer hover:bg-primary/5"
: "flex-initial cursor-pointer hover:bg-primary/5",
)}
>
<div
className={twMerge(
"flex flex-row bg-muted/10 border-muted border-b h-14 py-4 pr-4 pl-2 items-center gap-2"
"flex flex-row bg-muted/10 border-muted border-b h-14 py-4 pr-4 pl-2 items-center gap-2",
)}
onClick={toggle}
>
@@ -362,7 +362,7 @@ export const SectionHeaderAccordionItem = ({
<div
className={twMerge(
"overflow-y-scroll transition-all",
open ? " flex-grow" : "h-0 opacity-0"
open ? " flex-grow" : "h-0 opacity-0",
)}
>
{children}

View File

@@ -42,10 +42,10 @@ export const Breadcrumbs = ({ path: _path, backTo, onBack }: BreadcrumbsProps) =
return {
last,
href,
string
string,
};
}),
[path, loc]
[path, loc],
);
return (
@@ -82,9 +82,9 @@ const CrumbsMobile = ({ crumbs }) => {
() =>
crumbs.slice(1, -1).map((c) => ({
label: c.string,
href: c.href
href: c.href,
})),
[crumbs]
[crumbs],
);
const onClick = useEvent((item) => navigate(`~/${item.href}`));

View File

@@ -46,10 +46,10 @@ export const Breadcrumbs2 = ({ path: _path, backTo, onBack }: Breadcrumbs2Props)
return {
last,
...p
...p,
};
}),
[path]
[path],
);
return (
@@ -86,9 +86,9 @@ const CrumbsMobile = ({ crumbs }) => {
() =>
crumbs.slice(1, -1).map((c) => ({
label: c.string,
href: c.href
href: c.href,
})),
[crumbs]
[crumbs],
);
const onClick = useEvent((item) => navigate(`~/${item.href}`));

View File

@@ -8,7 +8,7 @@ import {
TbPhoto,
TbSelector,
TbUser,
TbX
TbX,
} from "react-icons/tb";
import { useAuth, useBkndWindowContext } from "ui/client";
import { useBknd } from "ui/client/bknd";
@@ -47,10 +47,10 @@ export function HeaderNavigation() {
{ label: "Data", href: "/data", Icon: TbDatabase },
{ label: "Auth", href: "/auth", Icon: TbFingerprint },
{ label: "Media", href: "/media", Icon: TbPhoto },
{ label: "Flows", href: "/flows", Icon: TbHierarchy2 }
{ label: "Flows", href: "/flows", Icon: TbHierarchy2 },
];
const activeItem = items.find((item) =>
item.exact ? location === item.href : location.startsWith(item.href)
item.exact ? location === item.href : location.startsWith(item.href),
);
const handleItemClick = useEvent((item) => {
@@ -158,7 +158,7 @@ function UserMenu() {
}
const items: DropdownItem[] = [
{ label: "Settings", onClick: () => navigate("/settings"), icon: IconSettings }
{ label: "Settings", onClick: () => navigate("/settings"), icon: IconSettings },
];
if (config.auth.enabled) {
@@ -168,7 +168,7 @@ function UserMenu() {
items.push({
label: `Logout ${auth.user.email}`,
onClick: handleLogout,
icon: IconKeyOff
icon: IconKeyOff,
});
}
}
@@ -200,7 +200,7 @@ function UserMenuThemeToggler() {
className="w-full"
data={[
{ value: "light", label: "Light" },
{ value: "dark", label: "Dark" }
{ value: "dark", label: "Dark" },
]}
value={theme}
onChange={toggle}

View File

@@ -12,7 +12,7 @@ import {
TagsInput,
TextInput,
Textarea,
createTheme
createTheme,
} from "@mantine/core";
import { twMerge } from "tailwind-merge";
@@ -26,7 +26,7 @@ export function createMantineTheme(scheme: "light" | "dark"): {
const dark = !light;
const baseComboboxProps: ComboboxProps = {
offset: 2,
transitionProps: { transition: "pop", duration: 75 }
transitionProps: { transition: "pop", duration: 75 },
};
const input =
@@ -39,97 +39,97 @@ export function createMantineTheme(scheme: "light" | "dark"): {
vars: (theme, props) => ({
// https://mantine.dev/styles/styles-api/
root: {
"--button-height": "auto"
}
"--button-height": "auto",
},
}),
classNames: (theme, props) => ({
root: twMerge("px-3 py-2 rounded-md h-auto")
root: twMerge("px-3 py-2 rounded-md h-auto"),
}),
defaultProps: {
size: "md",
variant: light ? "filled" : "white"
}
variant: light ? "filled" : "white",
},
}),
Switch: Switch.extend({
defaultProps: {
size: "md",
color: light ? "dark" : "blue"
}
color: light ? "dark" : "blue",
},
}),
Select: Select.extend({
classNames: (theme, props) => ({
//input: "focus:border-primary/50 bg-transparent disabled:text-primary",
input,
dropdown: `bknd-admin ${scheme} bg-background border-primary/20`
dropdown: `bknd-admin ${scheme} bg-background border-primary/20`,
}),
defaultProps: {
checkIconPosition: "right",
comboboxProps: baseComboboxProps
}
comboboxProps: baseComboboxProps,
},
}),
TagsInput: TagsInput.extend({
defaultProps: {
comboboxProps: baseComboboxProps
}
comboboxProps: baseComboboxProps,
},
}),
Radio: Radio.extend({
defaultProps: {
classNames: {
body: "items-center"
}
}
body: "items-center",
},
},
}),
TextInput: TextInput.extend({
classNames: (theme, props) => ({
wrapper: "leading-none",
input
})
input,
}),
}),
NumberInput: NumberInput.extend({
classNames: (theme, props) => ({
wrapper: "leading-none",
input
})
input,
}),
}),
Textarea: Textarea.extend({
classNames: (theme, props) => ({
wrapper: "leading-none",
input
})
input,
}),
}),
Modal: Modal.extend({
classNames: (theme, props) => ({
...props.classNames,
root: `bknd-admin ${scheme} ${props.className ?? ""}`,
content: "!bg-background !rounded-lg !select-none",
overlay: "!backdrop-blur-sm"
})
overlay: "!backdrop-blur-sm",
}),
}),
Tabs: Tabs.extend({
classNames: (theme, props) => ({
tab: "data-[active=true]:border-primary"
})
tab: "data-[active=true]:border-primary",
}),
}),
Menu: Menu.extend({
defaultProps: {
offset: 2
offset: 2,
},
classNames: (theme, props) => ({
dropdown: "!rounded-lg !px-1",
item: "!rounded-md !text-[14px]"
})
item: "!rounded-md !text-[14px]",
}),
}),
SegmentedControl: SegmentedControl.extend({
classNames: (theme, props) => ({
root: light ? "bg-primary/5" : "bg-lightest/60",
indicator: light ? "bg-background" : "bg-primary/15"
})
})
indicator: light ? "bg-background" : "bg-primary/15",
}),
}),
},
primaryColor: "dark",
primaryShade: 9
primaryShade: 9,
}),
forceColorScheme: scheme
forceColorScheme: scheme,
};
}

View File

@@ -9,37 +9,37 @@ export const routes = {
entity: {
list: (entity: string) => `/entity/${entity}`,
create: (entity: string) => `/entity/${entity}/create`,
edit: (entity: string, id: PrimaryFieldType) => `/entity/${entity}/edit/${id}`
edit: (entity: string, id: PrimaryFieldType) => `/entity/${entity}/edit/${id}`,
},
schema: {
root: () => "/schema",
entity: (entity: string) => `/schema/entity/${entity}`
}
entity: (entity: string) => `/schema/entity/${entity}`,
},
},
auth: {
root: () => "/auth",
users: {
list: () => "/users",
edit: (id: PrimaryFieldType) => `/users/edit/${id}`
edit: (id: PrimaryFieldType) => `/users/edit/${id}`,
},
roles: {
list: () => "/roles",
edit: (role: string) => `/roles/edit/${role}`
edit: (role: string) => `/roles/edit/${role}`,
},
settings: () => "/settings",
strategies: () => "/strategies"
strategies: () => "/strategies",
},
flows: {
root: () => "/flows",
flows: {
list: () => "/",
edit: (id: PrimaryFieldType) => `/flow/${id}`
}
edit: (id: PrimaryFieldType) => `/flow/${id}`,
},
},
settings: {
root: () => "/settings",
path: (path: string[]) => `/settings/${path.join("/")}`
}
path: (path: string[]) => `/settings/${path.join("/")}`,
},
};
export function withQuery(url: string, query: object) {
@@ -70,7 +70,7 @@ export function useNavigate() {
transition?: boolean;
}
| { reload: true }
| { target: string }
| { target: string },
) => {
const wrap = (fn: () => void) => {
fn();
@@ -97,11 +97,11 @@ export function useNavigate() {
const _url = options?.absolute ? `~/${basepath}${url}`.replace(/\/+/g, "/") : url;
navigate(options?.query ? withQuery(_url, options?.query) : _url, {
replace: options?.replace,
state: options?.state
state: options?.state,
});
});
},
location
location,
] as const;
}
@@ -110,7 +110,7 @@ export function useGoBack(
options?: {
native?: boolean;
absolute?: boolean;
}
},
) {
const { app } = useBknd();
const basepath = app.getAdminConfig().basepath;
@@ -153,6 +153,6 @@ export function useGoBack(
return {
same,
canGoBack,
goBack
goBack,
};
}

View File

@@ -7,7 +7,7 @@ import "./styles.css";
ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
<Admin withProvider />
</React.StrictMode>
</React.StrictMode>,
);
// REGISTER ERROR OVERLAY

View File

@@ -25,7 +25,7 @@ export function DebugModal({ innerProps }: ContextModalProps<DebugProps>) {
value: item,
expand: 10,
showCopy: true,
...jsonViewerProps
...jsonViewerProps,
};
});
@@ -58,7 +58,7 @@ export function DebugModal({ innerProps }: ContextModalProps<DebugProps>) {
// @ts-expect-error
...tabs[Object.keys(tabs)[0]],
// @ts-expect-error
title: tabs[Object.keys(tabs)[0]].label
title: tabs[Object.keys(tabs)[0]].label,
})
)}
</div>
@@ -70,6 +70,6 @@ DebugModal.modalProps = {
withCloseButton: false,
size: "lg",
classNames: {
body: "!p-0"
}
body: "!p-0",
},
} satisfies Omit<ModalProps, "opened" | "onClose">;

View File

@@ -4,7 +4,7 @@ import type { ReactNode } from "react";
export function OverlayModal({
context,
id,
innerProps: { content }
innerProps: { content },
}: ContextModalProps<{ content?: ReactNode }>) {
return content;
}
@@ -17,6 +17,6 @@ OverlayModal.modalProps = {
root: "bknd-admin",
content: "text-center justify-center",
title: "font-bold !text-md",
body: "py-3 px-5 gap-4 flex flex-col"
}
body: "py-3 px-5 gap-4 flex flex-col",
},
};

View File

@@ -3,7 +3,7 @@ import { Button } from "ui/components/buttons/Button";
import {
JsonSchemaForm,
type JsonSchemaFormProps,
type JsonSchemaFormRef
type JsonSchemaFormRef,
} from "ui/components/form/json-schema";
import type { ContextModalProps } from "@mantine/modals";
@@ -15,14 +15,14 @@ type Props = JsonSchemaFormProps & {
data: any,
context: {
close: () => void;
}
},
) => void | Promise<void>;
};
export function SchemaFormModal({
context,
id,
innerProps: { schema, uiSchema, onSubmit, autoCloseAfterSubmit }
innerProps: { schema, uiSchema, onSubmit, autoCloseAfterSubmit },
}: ContextModalProps<Props>) {
const [valid, setValid] = useState(false);
const formRef = useRef<JsonSchemaFormRef>(null);
@@ -49,7 +49,7 @@ export function SchemaFormModal({
setSubmitting(true);
await onSubmit?.(formRef.current?.formData(), {
close: handleClose,
setError
setError,
});
setSubmitting(false);
@@ -90,6 +90,6 @@ SchemaFormModal.modalProps = {
header: "!bg-lightest !py-3 px-5 !h-auto !min-h-px",
content: "rounded-lg select-none",
title: "!font-bold !text-md",
body: "!p-0"
}
body: "!p-0",
},
};

View File

@@ -17,6 +17,6 @@ TestModal.modalProps = {
header: "!bg-primary/5 border-b border-b-muted !py-3 px-5 !h-auto !min-h-px",
content: "rounded-lg select-none",
title: "font-bold !text-md",
body: "py-3 px-5 gap-4 flex flex-col"
}
body: "py-3 px-5 gap-4 flex flex-col",
},
};

View File

@@ -12,7 +12,7 @@ const modals = {
debug: DebugModal,
form: SchemaFormModal,
overlay: OverlayModal,
dataCreate: CreateModal
dataCreate: CreateModal,
};
declare module "@mantine/modals" {
@@ -28,7 +28,7 @@ export function BkndModalsProvider({ children }) {
function open<Modal extends keyof typeof modals>(
modal: Modal,
innerProps: ComponentProps<(typeof modals)[Modal]>["innerProps"],
{ title: _title, ...modalProps }: Partial<ModalProps> = {}
{ title: _title, ...modalProps }: Partial<ModalProps> = {},
) {
const title = _title ?? modals[modal].defaultTitle ?? undefined;
const cmpModalProps = modals[modal].modalProps ?? {};
@@ -37,12 +37,12 @@ function open<Modal extends keyof typeof modals>(
...modalProps,
...cmpModalProps,
modal,
innerProps
innerProps,
};
openContextModal(props);
return {
close: () => close(modal),
closeAll: $modals.closeAll
closeAll: $modals.closeAll,
};
}
@@ -56,5 +56,5 @@ export const bkndModals = {
},
open,
close,
closeAll: $modals.closeAll
closeAll: $modals.closeAll,
};

View File

@@ -11,7 +11,7 @@ export function useCreateUserModal() {
const open = async () => {
const loading = bkndModals.open("overlay", {
content: "Loading..."
content: "Loading...",
});
const schema = await api.auth.actionSchema("password", "create");
@@ -23,8 +23,8 @@ export function useCreateUserModal() {
schema,
uiSchema: {
password: {
"ui:widget": "password"
}
"ui:widget": "password",
},
},
autoCloseAfterSubmit: false,
onSubmit: async (data, ctx) => {
@@ -41,11 +41,11 @@ export function useCreateUserModal() {
} else {
ctx.setError("Unknown error");
}
}
},
},
{
title: "Create User"
}
title: "Create User",
},
);
};

Some files were not shown because too many files have changed in this diff Show More