import { type ComponentMeta, DataProvider, registerComponent, usePlasmicCanvasContext } from "@plasmicapp/host"; import { usePlasmicQueryData } from "@plasmicapp/loader-react"; import { useApi, useEntityQuery } from "bknd/client"; import type { RepoQueryIn } from "bknd/data"; // biome-ignore lint/style/useImportType: import React from "react"; import { usePlasmicBkndContext } from "../../contexts/BkndContext"; type BkndDataProps = { children?: React.ReactNode; loading?: React.ReactNode; error?: React.ReactNode; empty?: React.ReactNode; setControlContextData?: (ctxData: { entities: string[]; fields: string[]; references: string[]; }) => void; className?: string; limit?: number; offset?: number; withRefs?: string[]; joinRefs?: string[]; dataName?: string; entityId?: number; entity?: string; select?: string[]; sortBy: string; sortDir: "asc" | "desc"; where?: string; mode?: "fetch" | "swr"; noLayout?: boolean; preview?: boolean; previewSlot?: "loading" | "error" | "empty"; }; const LoadingComponent = ({ loading }: { loading?: React.ReactNode }) => { return loading ? <>{loading} : <>Loading...; }; const ErrorComponent = ({ error }: { error?: React.ReactNode }) => { return error ? <>{error} : <>Error; }; const EmptyComponent = ({ empty }: { empty?: React.ReactNode }) => { return empty ? <>{empty} : <>No data; }; export function BkndData({ children, loading, error, empty, entity, setControlContextData, dataName, limit, offset, entityId, where, withRefs, joinRefs, sortBy = "id", sortDir = "asc", mode = "fetch", select = [], noLayout, preview, previewSlot, ...props }: BkndDataProps) { //console.log("--bknd data"); const inEditor = !!usePlasmicCanvasContext(); const plasmicContext = usePlasmicBkndContext(); if (inEditor && preview) { let Component: React.ReactNode; switch (previewSlot) { case "loading": Component = ; break; case "error": Component = ; break; case "empty": Component = ; break; } if (Component) { return noLayout ? Component :
{Component}
; } } let _where: any = undefined; if (where) { if (typeof where === "string") { try { _where = JSON.parse(where); } catch (e) {} } else { _where = where; } } const query = { select: select.length > 0 ? select : undefined, limit: entityId ? undefined : limit, offset: entityId ? undefined : offset, where: _where, sort: `${sortDir === "desc" ? "-" : ""}${sortBy}`, with: withRefs, join: joinRefs }; //console.log("---context", plasmicContext); if (plasmicContext.appConfig?.data?.entities) { const { entities, relations } = plasmicContext.appConfig.data; console.log("entities", entities); //setControlContextData?.({ entities, fields: ["id"] }); let fields: string[] = ["id"]; let references: string[] = []; if (entity && entity in entities) { fields = Object.keys(entities[entity]?.fields ?? {}); if (relations) { const rels = Object.values(relations).filter( // biome-ignore lint/suspicious/noDoubleEquals: (r: any) => r.source == entity ); // @ts-ignore references = rels?.map((r) => r.config?.mappedBy ?? r.target); //console.log("relations", relations, references); } } setControlContextData?.({ entities: Object.keys(entities), fields, references }); } if (!entity) { return
Select an entity
; } const modeProps: ModeProps = { loading, error, empty, dataName: dataName ?? entity ?? "data", entityId, entity, query, children }; const Component = mode === "swr" ? : ; return noLayout ? Component :
{Component}
; } type ModeProps = { entity: string; dataName: string; children?: React.ReactNode; loading?: React.ReactNode; error?: React.ReactNode; empty?: React.ReactNode; entityId?: number; query?: RepoQueryIn; }; const ModeFetch = ({ children, loading, error, empty, dataName, entityId, entity, query }: ModeProps) => { const api = useApi(); const endpoint = entityId ? api.data.readOne(entity, entityId, query) : api.data.readMany(entity, query); const { data, error: hasError, isLoading } = usePlasmicQueryData(endpoint.key(), async () => { const res = await endpoint.execute(); return res.data; }); if (isLoading) { return ; } if (hasError || !data) { return ; } if (data?.length === 0) { return ; } return ( {children} ); }; const ModeSWR = ({ children, loading, error, dataName, entityId, empty, entity }: ModeProps) => { const $q = useEntityQuery(entity, entityId); if ($q.isLoading) { return ; } if ($q.error) { return ; } if (!$q.data) { return ; } return ( {children} ); }; export function registerBkndData( loader?: { registerComponent: typeof registerComponent }, customMeta?: ComponentMeta ) { if (loader) { loader.registerComponent(BkndData, customMeta ?? BkndDataMeta); } else { registerComponent(BkndData, customMeta ?? BkndDataMeta); } } export const BkndDataMeta: ComponentMeta = { name: "BKND Data", importName: "BkndData", section: "BKND", importPath: "@bknd/plasmic", providesData: true, props: { entity: { type: "choice", options: (props, ctx) => ctx?.entities ?? [] }, dataName: { type: "string" }, entityId: { type: "number" }, select: { type: "choice", multiSelect: true, options: (props, ctx) => ctx?.fields ?? [] }, limit: { type: "number", defaultValue: 10, // @ts-ignore hidden: (props) => !!props.entityId, min: 0 }, offset: { type: "number", defaultValue: 0, // @ts-ignore hidden: (props) => !!props.entityId, min: 0 }, withRefs: { displayName: "With", type: "choice", multiSelect: true, options: (props, ctx) => ctx?.references ?? [] }, joinRefs: { displayName: "Join", type: "choice", multiSelect: true, options: (props, ctx) => ctx?.references ?? [] }, where: { type: "code", lang: "json" }, sortBy: { type: "choice", options: (props, ctx) => ctx?.fields ?? [] }, sortDir: { type: "choice", options: ["asc", "desc"], defaultValue: "asc" }, children: { type: "slot" }, loading: { type: "slot" }, error: { type: "slot" }, empty: { type: "slot" }, mode: { type: "choice", options: ["fetch", "swr"], defaultValue: "fetch", advanced: true }, noLayout: { type: "boolean", defaultValue: true, advanced: true }, preview: { type: "boolean", defaultValue: false, advanced: true }, previewSlot: { type: "choice", options: ["loading", "error", "empty"], hidden: (props: any) => props.preview !== true, advanced: true } } };