updated plasmic package to work with new hooks + added example

This commit is contained in:
dswbx
2024-12-18 08:41:11 +01:00
parent 1631bbb754
commit c4138ef823
34 changed files with 491 additions and 334 deletions

1
.gitignore vendored
View File

@@ -14,6 +14,7 @@ packages/media/.env
**/*/.env
**/*/.dev.vars
**/*/.wrangler
**/*/*.tgz
**/*/vite.config.ts.timestamp*
.history
**/*/.db/*

View File

@@ -3,7 +3,7 @@
"type": "module",
"sideEffects": false,
"bin": "./dist/cli/index.js",
"version": "0.3.3",
"version": "0.3.4-alpha1",
"scripts": {
"build:all": "bun run build && bun run build:cli",
"dev": "vite",

View File

@@ -16,6 +16,10 @@ export class SystemApi extends ModuleApi<any> {
};
}
readConfig() {
return this.get<{ version: number } & ModuleConfigs>("config");
}
readSchema(options?: { config?: boolean; secrets?: boolean }) {
return this.get<ApiSchemaResponse>("schema", {
config: options?.config ? 1 : 0,

View File

@@ -12,44 +12,38 @@ export type ClientProviderProps = {
};
export const ClientProvider = ({ children, baseUrl, user }: ClientProviderProps) => {
const [actualBaseUrl, setActualBaseUrl] = useState<string | null>(null);
//const [actualBaseUrl, setActualBaseUrl] = useState<string | null>(null);
const winCtx = useBkndWindowContext();
const _ctx_baseUrl = useBaseUrl();
let actualBaseUrl = baseUrl ?? _ctx_baseUrl ?? "";
try {
const _ctx_baseUrl = useBaseUrl();
if (_ctx_baseUrl) {
console.warn("wrapped many times");
setActualBaseUrl(_ctx_baseUrl);
if (!baseUrl) {
if (_ctx_baseUrl) {
actualBaseUrl = _ctx_baseUrl;
console.warn("wrapped many times, take from context", actualBaseUrl);
} else if (typeof window !== "undefined") {
actualBaseUrl = window.location.origin;
console.log("setting from window", actualBaseUrl);
}
}
} catch (e) {
console.error("error", e);
}
useEffect(() => {
// Only set base URL if running on the client side
if (typeof window !== "undefined") {
setActualBaseUrl(baseUrl || window.location.origin);
}
}, [baseUrl]);
if (!actualBaseUrl) {
// Optionally, return a fallback during SSR rendering
return null; // or a loader/spinner if desired
console.error("error .....", e);
}
const api = new Api({ host: actualBaseUrl, user: user ?? winCtx.user });
return (
<ClientContext.Provider value={{ baseUrl: actualBaseUrl, api }}>
<ClientContext.Provider value={{ baseUrl: api.baseUrl, api }}>
{children}
</ClientContext.Provider>
);
};
export const useApi = (host?: ApiOptions["host"]) => {
export const useApi = (host?: ApiOptions["host"]): Api => {
const context = useContext(ClientContext);
if (host && host !== context.baseUrl) {
return new Api({ host });
if (!context?.api || (host && host.length > 0 && host !== context.baseUrl)) {
return new Api({ host: host ?? "" });
}
return context.api;

View File

@@ -15,7 +15,7 @@ import {
} from "@mantine/core";
import { twMerge } from "tailwind-merge";
// default: https://github.com/mantinedev/mantine/blob/master/src/mantine-core/src/core/MantineProvider/default-theme.ts
// default: https://github.com/mantinedev/mantine/blob/master/packages/%40mantine/core/src/core/MantineProvider/default-theme.ts
export function createMantineTheme(scheme: "light" | "dark"): {
theme: ReturnType<typeof createTheme>;

BIN
bun.lockb

Binary file not shown.

1
examples/plasmic/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
.next

5
examples/plasmic/next-env.d.ts vendored Normal file
View File

@@ -0,0 +1,5 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information.

View File

@@ -0,0 +1,8 @@
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
/* config options here */
reactStrictMode: true,
};
export default nextConfig;

View File

@@ -0,0 +1,26 @@
{
"name": "plasmic-nextjs",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"@plasmicapp/loader-nextjs": "^1.0.409",
"bknd": "workspace:*",
"@bknd/plasmic": "workspace:*",
"next": "15.0.4",
"react": "^18.3.1",
"react-dom": "^18.3.1"
},
"devDependencies": {
"typescript": "^5",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"postcss": "^8"
}
}

View File

@@ -0,0 +1,8 @@
/** @type {import('postcss-load-config').Config} */
const config = {
plugins: {
tailwindcss: {},
},
};
export default config;

View File

@@ -0,0 +1,78 @@
import { PLASMIC } from "@/plasmic-init";
import {
type ComponentRenderData,
PlasmicComponent,
PlasmicRootProvider,
extractPlasmicQueryData
} from "@plasmicapp/loader-nextjs";
import type { GetServerSideProps } from "next";
// biome-ignore lint/suspicious/noShadowRestrictedNames: <explanation>
import Error from "next/error";
import { useRouter } from "next/router";
import * as React from "react";
export const getServerSideProps: GetServerSideProps = async (context) => {
const { catchall } = context.params ?? {};
// Convert the catchall param into a path string
const plasmicPath =
typeof catchall === "string"
? catchall
: Array.isArray(catchall)
? `/${catchall.join("/")}`
: "/";
const plasmicData = await PLASMIC.maybeFetchComponentData(plasmicPath);
if (!plasmicData) {
// This is some non-Plasmic catch-all page
return {
props: {}
};
}
// This is a path that Plasmic knows about.
const pageMeta = plasmicData.entryCompMetas[0];
// Cache the necessary data fetched for the page.
const queryCache = await extractPlasmicQueryData(
<PlasmicRootProvider
loader={PLASMIC}
prefetchedData={plasmicData}
pageRoute={pageMeta.path}
pageParams={pageMeta.params}
>
{/* @ts-ignore */}
<PlasmicComponent component={pageMeta.displayName} />
</PlasmicRootProvider>
);
// Pass the data in as props.
return {
props: { plasmicData, queryCache }
};
};
export default function CatchallPage(props: {
plasmicData?: ComponentRenderData;
queryCache?: Record<string, any>;
}) {
const { plasmicData, queryCache } = props;
const router = useRouter();
if (!plasmicData || plasmicData.entryCompMetas.length === 0) {
return <Error statusCode={404} />;
}
const pageMeta = plasmicData.entryCompMetas[0];
return (
// Pass in the data fetched in getStaticProps as prefetchedData
<PlasmicRootProvider
loader={PLASMIC}
prefetchedData={plasmicData}
prefetchedQueryData={queryCache}
pageRoute={pageMeta.path}
pageParams={pageMeta.params}
pageQuery={router.query}
>
{/* @ts-ignore */}
<PlasmicComponent component={pageMeta.displayName} />
</PlasmicRootProvider>
);
}

View File

@@ -0,0 +1,11 @@
import "@/styles/globals.css";
import { ClientProvider } from "bknd/client";
import type { AppProps } from "next/app";
export default function App({ Component, pageProps }: AppProps) {
return (
<ClientProvider baseUrl="http://localhost:3000">
<Component {...pageProps} />
</ClientProvider>
);
}

View File

@@ -0,0 +1,13 @@
import { Html, Head, Main, NextScript } from "next/document";
export default function Document() {
return (
<Html lang="en">
<Head />
<body className="antialiased">
<Main />
<NextScript />
</body>
</Html>
);
}

View File

@@ -0,0 +1,25 @@
// pages/admin/[[...admin]].tsx
import { withApi } from "bknd/adapter/nextjs";
import dynamic from "next/dynamic";
import "bknd/dist/styles.css";
/*export const config = {
runtime: "experimental-edge"
}*/
const Admin = dynamic(() => import("bknd/ui").then((mod) => mod.Admin), {
ssr: false,
});
export const getServerSideProps = withApi(async (context) => {
return {
props: {
user: context.api.getUser(),
},
};
});
export default function AdminPage() {
if (typeof document === "undefined") return null;
return <Admin withProvider config={{ basepath: "/admin" }} />;
}

View File

@@ -0,0 +1,16 @@
import { serve } from "bknd/adapter/nextjs";
export const config = {
runtime: "edge",
unstable_allowDynamic: ["**/*.js"]
};
export default serve({
connection: {
type: "libsql",
config: {
url: process.env.DB_URL!,
authToken: process.env.DB_TOKEN!
}
}
});

View File

@@ -0,0 +1,7 @@
import * as React from 'react';
import { PlasmicCanvasHost } from '@plasmicapp/loader-nextjs';
import { PLASMIC } from '@/plasmic-init';
export default function PlasmicHost() {
return PLASMIC && <PlasmicCanvasHost />;
}

View File

@@ -0,0 +1,6 @@
import { useApi } from "bknd/client";
export default function Test() {
const api = useApi(undefined);
return <div>{api.baseUrl}</div>;
}

View File

@@ -0,0 +1,17 @@
import { initPlasmicLoader } from "@plasmicapp/loader-nextjs";
import { loader } from "@bknd/plasmic";
export const PLASMIC = initPlasmicLoader({
projects: [
{
id: process.env.PLASMIC_ID!,
token: process.env.PLASMIC_TOKEN!,
}
],
preview: true, //process.env.NODE_ENV === "development",
})
loader(PLASMIC);
/*
PLASMIC.registerComponent(BkndData, BkndDataMeta);
PLASMIC.registerGlobalContext(BkndContext, BkndContextMeta as any);*/

View File

@@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

View File

@@ -0,0 +1,18 @@
import type { Config } from "tailwindcss";
export default {
content: [
"./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
"./src/components/**/*.{js,ts,jsx,tsx,mdx}",
"./src/app/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
extend: {
colors: {
background: "var(--background)",
foreground: "var(--foreground)",
},
},
},
plugins: [],
} satisfies Config;

View File

@@ -0,0 +1,23 @@
{
"compilerOptions": {
"target": "ES2017",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"noImplicitAny": false,
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]
}

View File

@@ -1,23 +0,0 @@
import type { CodeComponentMeta } from "@plasmicapp/host";
import { Link } from "wouter";
export function WouterLink({ href, className, children, ...props }) {
return (
<Link href={href ?? "#"} className={className} {...props}>
{children}
</Link>
);
}
export const WouterLinkMeta: CodeComponentMeta<any> = {
name: "WouterLink",
importPath: import.meta.dir,
props: {
href: {
type: "href",
},
children: {
type: "slot",
},
},
};

View File

@@ -1,4 +0,0 @@
export { loader as loadBkndComponents, CatchAllPage, createWouterPlasmicApp } from "./loader";
export * from "./components";
export * from "./contexts";

View File

@@ -1,109 +0,0 @@
import { PlasmicCanvasHost, type registerComponent } from "@plasmicapp/host";
import {
type ComponentRenderData,
PlasmicComponent,
type PlasmicComponentLoader,
PlasmicRootProvider
} from "@plasmicapp/loader-react";
import { forwardRef, useEffect, useState } from "react";
import { Link, Route, Router, Switch } from "wouter";
import {
BkndData,
BkndDataMeta,
Image,
ImageMeta,
LazyRender,
LazyRenderMeta,
WouterLink,
WouterLinkMeta
} from "./components";
import { BkndContext, BkndContextMeta } from "./contexts";
export function loader(PLASMIC: PlasmicComponentLoader) {
PLASMIC.registerComponent(BkndData, BkndDataMeta);
PLASMIC.registerComponent(WouterLink, WouterLinkMeta);
PLASMIC.registerComponent(Image, ImageMeta);
PLASMIC.registerComponent(LazyRender, LazyRenderMeta);
PLASMIC.registerGlobalContext(BkndContext, BkndContextMeta as any);
}
const CustomLink = forwardRef<any, any>((props, ref) => {
//console.log("rendering custom link", props);
//return null;
if ("data-replace" in props) {
return <a ref={ref} {...props} />;
}
//return <a ref={ref} {...props} />;
// @ts-ignore it's because of the link
return <Link ref={ref} {...props} />;
});
const Wrapper = ({ children }) => {
return (
<div
style={{
width: "100wh",
height: "100vh",
display: "flex",
alignItems: "center",
justifyContent: "center"
}}
>
<div style={{ opacity: 0.5, textTransform: "uppercase" }}>{children}</div>
</div>
);
};
export function CatchAllPage({
PLASMIC,
prefix = ""
}: { PLASMIC: PlasmicComponentLoader; prefix?: string }) {
const [loading, setLoading] = useState(true);
const [pageData, setPageData] = useState<ComponentRenderData | null>(null);
//const params = useParams();
const pathname = location.pathname.replace(prefix, "");
const path = pathname.length === 0 ? "/" : pathname;
//console.log("path", path, params);
useEffect(() => {
async function load() {
const pageData = await PLASMIC.maybeFetchComponentData(path);
//console.log("pageData", pageData);
setPageData(pageData);
setLoading(false);
}
load();
}, []);
if (loading) {
return <Wrapper>Loading ...</Wrapper>;
}
if (!pageData) {
return <Wrapper>Not found</Wrapper>;
}
const pageMeta = pageData.entryCompMetas[0];
// The page will already be cached from the `load` call above.
return (
<PlasmicRootProvider loader={PLASMIC} pageParams={pageMeta.params} Link={CustomLink}>
<PlasmicComponent component={path} />
</PlasmicRootProvider>
);
}
export function createWouterPlasmicApp(PLASMIC: PlasmicComponentLoader, prefix = "") {
return function App() {
return (
<Router base={prefix}>
<Switch>
<Route path="/host" component={PlasmicCanvasHost as any} />
<Route
path="/*"
component={() => <CatchAllPage PLASMIC={PLASMIC} prefix={prefix} />}
/>
</Switch>
</Router>
);
};
}

View File

@@ -1,5 +1,6 @@
{
"name": "@bknd/plasmic",
"version": "0.3.4-alpha1",
"type": "module",
"sideEffects": false,
"scripts": {
@@ -8,41 +9,61 @@
"build:only": "rm -rf dist && bun tsup",
"types": "bun tsc -p tsconfig.json --noEmit --skipLibCheck",
"build:types": "bun tsc --emitDeclarationOnly",
"updater": "bun x npm-check-updates -ui"
"updater": "bun x npm-check-updates -ui",
"pack": "rm -rf *.tgz && npm pack && mv *.tgz latest.tgz",
"prepublishOnly": "bun run build"
},
"dependencies": {
"wouter": "^3.3.5"
"publishConfig": {
"access": "public"
},
"dependencies": {},
"devDependencies": {
"@types/bun": "latest",
"bknd": "workspace:*",
"tsdx": "^0.14.1",
"typescript": "^5.0.0"
},
"peerDependencies": {
"@plasmicapp/host": ">=1.0.0",
"bknd": "workspace:*",
"bknd": "*",
"react": ">=18",
"react-dom": ">=18"
"react-dom": ">=18",
"@plasmicapp/host": ">=1.0.0",
"@plasmicapp/query": ">=0.1.0"
},
"tsup": {
"entry": ["index.ts"],
"minify": true,
"entry": [
"src/index.ts"
],
"minify": false,
"clean": true,
"external": ["react", "react-dom", "@plasmicapp/host", "@plasmicapp/loader-react", "@plasmicapp/loader-core"],
"format": ["esm"],
"external": [
"react",
"react-dom",
"@plasmicapp/host",
"@plasmicapp/query",
"swr"
],
"format": [
"esm",
"cjs"
],
"platform": "browser",
"shims": true,
"bundle": true,
"metafile": true,
"splitting": false,
"splitting": true,
"sourceMap": true,
"outDir": "dist"
},
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js",
"require": "./dist/index.js"
}
},
"files": ["dist"]
"types": "dist/index.d.ts",
"module": "dist/index.js",
"main": "dist/index.cjs",
"files": [
"dist",
"README.md",
"!dist/*.tsbuildinfo",
"!dist/*.map",
"!dist/**/*.map",
"!dist/metafile*",
"!dist/**/metafile*"
]
}

View File

@@ -1,6 +1,7 @@
"use client";
import type { CodeComponentMeta } from "@plasmicapp/host";
import registerComponent, { type ComponentMeta } from "@plasmicapp/host/registerComponent";
// biome-ignore lint/style/useImportType: <explanation>
import React from "react";
//import { PlasmicCanvasContext } from "@plasmicapp/loader-react";
import { useContext, useEffect, useRef, useState } from "react";
@@ -39,7 +40,7 @@ function numeric(value: number | string): number {
function getDimensionDefaults(
width: number | string | undefined,
height: number | string | undefined,
ratio: number | undefined,
ratio: number | undefined
) {
let _width = width;
let _height = height;
@@ -61,7 +62,7 @@ function getDimensionDefaults(
function getPlaceholderStyle(
width: number | string | undefined,
height: number | string | undefined,
ratio: number | undefined,
ratio: number | undefined
) {
let paddingBottom = 0;
if (width && height) {
@@ -73,7 +74,7 @@ function getPlaceholderStyle(
}
return {
paddingBottom: paddingBottom + "%",
paddingBottom: paddingBottom + "%"
};
}
@@ -126,7 +127,7 @@ export const Image: React.FC<ImageProps> = ({
}
});
},
{ threshold: loadTreshold },
{ threshold: loadTreshold }
);
if (imgRef.current) {
observer.observe(imgRef.current);
@@ -150,7 +151,7 @@ export const Image: React.FC<ImageProps> = ({
const {
width: _width,
height: _height,
ratio: _ratio,
ratio: _ratio
} = getDimensionDefaults(width, height, ratio);
const imgStyle: any = {
@@ -163,7 +164,7 @@ export const Image: React.FC<ImageProps> = ({
height: "auto",
//height: _height || "auto",
//height: !transitioned ? _height || "auto" : "auto",
opacity: forceLoad || loaded ? 1 : 0,
opacity: forceLoad || loaded ? 1 : 0
};
const placeholderStyle: any = {
@@ -174,7 +175,7 @@ export const Image: React.FC<ImageProps> = ({
width: _width || "100%",
height: 0,
//height: transitioned ? "auto" : 0,
...getPlaceholderStyle(_width, _height, _ratio),
...getPlaceholderStyle(_width, _height, _ratio)
};
const wrapperStyle: any = {
@@ -186,7 +187,7 @@ export const Image: React.FC<ImageProps> = ({
lineHeight: 0,
//height: _height,
maxWidth: "100%",
maxHeight: "100%",
maxHeight: "100%"
};
if (loaded) {
wrapperStyle.height = "auto";
@@ -213,13 +214,24 @@ export const Image: React.FC<ImageProps> = ({
);
};
export const ImageMeta: CodeComponentMeta<React.ComponentType<ImageProps>> = {
export function registerImage(
loader?: { registerComponent: typeof registerComponent },
customMeta?: ComponentMeta<ImageProps>
) {
if (loader) {
loader.registerComponent(Image, customMeta ?? ImageMeta);
} else {
registerComponent(Image, customMeta ?? ImageMeta);
}
}
export const ImageMeta: CodeComponentMeta<ImageProps> = {
name: "ImageLazy",
importPath: import.meta.dir,
importPath: "@bknd/plasmic",
props: {
src: {
type: "imageUrl",
displayName: "Image",
displayName: "Image"
},
alt: "string",
width: "number",
@@ -230,14 +242,14 @@ export const ImageMeta: CodeComponentMeta<React.ComponentType<ImageProps>> = {
//backgroundColor: "color",
transitionSpeed: {
type: "number",
helpText: "How fast image should fade in. Default is 200 (ms).",
helpText: "How fast image should fade in. Default is 200 (ms)."
},
loadTreshold: {
type: "number",
displayName: "Treshold",
//defaultValue: 0.1,
helpText:
"Number between 0 and 1. Default is 0.1. Determines how much of the image must be in viewport before it gets loaded",
},
},
"Number between 0 and 1. Default is 0.1. Determines how much of the image must be in viewport before it gets loaded"
}
}
};

View File

@@ -1,4 +1,7 @@
import type { CodeComponentMeta } from "@plasmicapp/host";
import registerComponent, { type ComponentMeta } from "@plasmicapp/host/registerComponent";
// biome-ignore lint/style/useImportType: <explanation>
import React from "react";
import { useEffect, useRef, useState } from "react";
interface LazyRenderProps {
@@ -22,7 +25,7 @@ export const LazyRender: React.FC<LazyRenderProps> = ({
threshold = 0.1,
delay = 0,
fallback = <DefaultFallback />,
onBecomesVisible,
onBecomesVisible
}) => {
const [isVisible, setIsVisible] = useState(forceLoad);
const ref = useRef<HTMLDivElement>(null);
@@ -43,7 +46,7 @@ export const LazyRender: React.FC<LazyRenderProps> = ({
}
const observerOptions: IntersectionObserverInit = {
threshold: threshold < 1 ? threshold : 0.1,
threshold: threshold < 1 ? threshold : 0.1
};
const observerCallback: IntersectionObserverCallback = (entries) => {
@@ -74,38 +77,49 @@ export const LazyRender: React.FC<LazyRenderProps> = ({
);
};
export const LazyRenderMeta: CodeComponentMeta<React.ComponentType<LazyRenderProps>> = {
export function registerLazyRender(
loader?: { registerComponent: typeof registerComponent },
customMeta?: ComponentMeta<LazyRenderProps>
) {
if (loader) {
loader.registerComponent(LazyRender, customMeta ?? LazyRenderMeta);
} else {
registerComponent(LazyRender, customMeta ?? LazyRenderMeta);
}
}
export const LazyRenderMeta: CodeComponentMeta<LazyRenderProps> = {
name: "LazyRender",
importPath: import.meta.dir,
importPath: "@bknd/plasmic",
props: {
forceLoad: {
type: "boolean",
defaultValue: false,
defaultValue: false
},
forceFallback: {
type: "boolean",
defaultValue: false,
defaultValue: false
},
threshold: {
type: "number",
defaultValue: 0.1,
defaultValue: 0.1
},
fallback: {
type: "slot",
type: "slot"
//allowedComponents: ["*"],
},
delay: {
type: "number",
defaultValue: 0,
defaultValue: 0
},
onBecomesVisible: {
type: "code",
lang: "javascript",
lang: "javascript"
},
children: {
type: "slot",
type: "slot"
//allowedComponents: ["*"],
},
},
}
}
};

View File

@@ -1,11 +1,13 @@
import { type CodeComponentMeta, DataProvider, usePlasmicCanvasContext } from "@plasmicapp/host";
import { DataProvider, usePlasmicCanvasContext } from "@plasmicapp/host";
import registerComponent, { type ComponentMeta } from "@plasmicapp/host/registerComponent";
import { usePlasmicQueryData } from "@plasmicapp/query";
import { useApi, useEntityQuery } from "bknd/client";
import type { RepoQuery } from "bknd/data";
import { useEntities, useEntity } from "bknd/ui";
import { encodeSearch } from "bknd/utils";
import { useContext, useEffect, useState } from "react";
// biome-ignore lint/style/useImportType: <explanation>
import React from "react";
import { usePlasmicBkndContext } from "../../contexts/BkndContext";
type BkndEntitiesProps = {
type BkndDataProps = {
children?: React.ReactNode;
loading?: React.ReactNode;
error?: React.ReactNode;
@@ -23,10 +25,11 @@ type BkndEntitiesProps = {
dataName?: string;
entityId?: number;
entity?: string;
select?: string[];
sortBy: string;
sortDir: "asc" | "desc";
where?: string;
mode?: "fetch" | "react-query";
mode?: "fetch" | "swr";
noLayout?: boolean;
preview?: boolean;
previewSlot?: "loading" | "error" | "empty";
@@ -61,11 +64,13 @@ export function BkndData({
sortBy = "id",
sortDir = "asc",
mode = "fetch",
select = [],
noLayout,
preview,
previewSlot,
...props
}: BkndEntitiesProps) {
}: BkndDataProps) {
//console.log("--bknd data");
const inEditor = !!usePlasmicCanvasContext();
const plasmicContext = usePlasmicBkndContext();
@@ -100,6 +105,7 @@ export function BkndData({
}
const query = {
select: select.length > 0 ? select : undefined,
limit: entityId ? undefined : limit,
offset: entityId ? undefined : offset,
where: _where,
@@ -108,7 +114,7 @@ export function BkndData({
join: joinRefs
};
console.log("---context", plasmicContext);
//console.log("---context", plasmicContext);
if (plasmicContext.appConfig?.data?.entities) {
const { entities, relations } = plasmicContext.appConfig.data;
console.log("entities", entities);
@@ -149,8 +155,7 @@ export function BkndData({
children
};
const Component =
mode === "react-query" ? <ModeReactQuery {...modeProps} /> : <ModeFetch {...modeProps} />;
const Component = mode === "swr" ? <ModeSWR {...modeProps} /> : <ModeFetch {...modeProps} />;
return noLayout ? Component : <div className={props.className}>{Component}</div>;
}
@@ -175,32 +180,19 @@ const ModeFetch = ({
entity,
query
}: ModeProps) => {
const [data, setData] = useState<any[]>([]);
const [isLoading, setLoading] = useState(true);
const [hasError, setError] = useState<string>();
const plasmicContext = usePlasmicBkndContext();
const basepath = "/api/data";
const path = entityId ? `${basepath}/${entity}/${entityId}` : `${basepath}/${entity}`;
console.log("query", path, query);
const url = `${plasmicContext.baseUrl}${path}?${encodeSearch(query)}`;
useEffect(() => {
(async () => {
try {
const res = await fetch(url);
const result = (await res.json()) as any;
//console.log("result", result);
setData(result.data);
setLoading(false);
setError(undefined);
} catch (e) {
console.error(e);
setError(String(e));
setLoading(false);
}
})();
}, [url]);
const api = useApi();
const endpoint = entityId
? api.data.readOne(entity, entityId, query)
: api.data.readMany(entity, query);
console.log("--data", { name: dataName ?? entity ?? "data", data, isLoading, hasError });
const {
data,
error: hasError,
isLoading
} = usePlasmicQueryData(endpoint.key(), async () => {
const res = await endpoint.execute();
return res.data;
});
if (isLoading) {
return <LoadingComponent loading={loading} />;
@@ -213,7 +205,6 @@ const ModeFetch = ({
if (data.length === 0) {
return <EmptyComponent empty={empty} />;
}
console.log("--here1");
return (
<DataProvider name={dataName ?? entity ?? "data"} data={data}>
@@ -222,85 +213,48 @@ const ModeFetch = ({
);
};
const ModeReactQuery = (props: ModeProps) => {
return props.entityId ? (
<ModeReactQuerySingle {...props} />
) : (
<ModeReactQueryMultiple {...props} />
);
};
const ModeSWR = ({ children, loading, error, dataName, entityId, empty, entity }: ModeProps) => {
const $q = useEntityQuery(entity, entityId);
const ModeReactQuerySingle = ({
children,
loading,
error,
dataName,
entityId,
empty,
entity
}: ModeProps) => {
const container = useEntity(entity, entityId);
const { isLoading, isError } = container.status.fetch;
if (isLoading) {
if ($q.isLoading) {
return <LoadingComponent loading={loading} />;
}
if (isError) {
if ($q.error) {
return <ErrorComponent error={error} />;
}
if (!container.data) {
if (!$q.data) {
return <EmptyComponent empty={empty} />;
}
return (
<DataProvider name={dataName ?? entity ?? "data"} data={container.data}>
<DataProvider name={dataName ?? entity ?? "data"} data={$q.data}>
{children}
</DataProvider>
);
};
const ModeReactQueryMultiple = ({
children,
loading,
error,
empty,
dataName,
entity,
query
}: ModeProps) => {
const container = useEntities(entity, query);
const { isLoading, isError } = container.status.fetch;
if (isLoading) {
return <LoadingComponent loading={loading} />;
export function registerBkndData(
loader?: { registerComponent: typeof registerComponent },
customMeta?: ComponentMeta<BkndDataProps>
) {
if (loader) {
loader.registerComponent(BkndData, customMeta ?? BkndDataMeta);
} else {
registerComponent(BkndData, customMeta ?? BkndDataMeta);
}
}
if (isError) {
return <ErrorComponent error={error} />;
}
if (!container.data || container.data.length === 0) {
return <EmptyComponent empty={empty} />;
}
return (
<DataProvider name={dataName ?? entity ?? "data"} data={container.data}>
{children}
</DataProvider>
);
};
export const BkndDataMeta: CodeComponentMeta<React.ComponentType<BkndEntitiesProps>> = {
export const BkndDataMeta: ComponentMeta<BkndDataProps> = {
name: "BKND Data",
section: "BKND",
importPath: import.meta.dir,
importPath: "@bknd/plasmic",
providesData: true,
props: {
entity: {
type: "choice",
options: (props, ctx) => ctx.entities
options: (props, ctx) => ctx?.entities ?? []
},
dataName: {
type: "string"
@@ -308,6 +262,10 @@ export const BkndDataMeta: CodeComponentMeta<React.ComponentType<BkndEntitiesPro
entityId: {
type: "number"
},
select: {
type: "choice",
options: (props, ctx) => ctx?.fields ?? []
},
limit: {
type: "number",
defaultValue: 10,
@@ -326,13 +284,13 @@ export const BkndDataMeta: CodeComponentMeta<React.ComponentType<BkndEntitiesPro
displayName: "With",
type: "choice",
multiSelect: true,
options: (props, ctx) => ctx.references
options: (props, ctx) => ctx?.references ?? []
},
joinRefs: {
displayName: "Join",
type: "choice",
multiSelect: true,
options: (props, ctx) => ctx.references
options: (props, ctx) => ctx?.references ?? []
},
where: {
type: "code",
@@ -340,7 +298,7 @@ export const BkndDataMeta: CodeComponentMeta<React.ComponentType<BkndEntitiesPro
},
sortBy: {
type: "choice",
options: (props, ctx) => ctx.fields
options: (props, ctx) => ctx?.fields ?? []
},
sortDir: {
type: "choice",
@@ -361,7 +319,7 @@ export const BkndDataMeta: CodeComponentMeta<React.ComponentType<BkndEntitiesPro
},
mode: {
type: "choice",
options: ["fetch", "react-query"],
options: ["fetch", "swr"],
defaultValue: "fetch",
advanced: true
},

View File

@@ -1,4 +1,3 @@
export { BkndData, BkndDataMeta } from "./data/BkndData";
export { WouterLink, WouterLinkMeta } from "./WouterLink";
export { Image, ImageMeta } from "./Image";
export { LazyRender, LazyRenderMeta } from "./LazyRender";

View File

@@ -1,6 +1,12 @@
import { DataProvider, GlobalActionsProvider, usePlasmicCanvasContext } from "@plasmicapp/host";
import registerGlobalContext, {
type GlobalContextMeta
} from "@plasmicapp/host/registerGlobalContext";
import type { AppConfig } from "bknd";
import { ClientProvider, useAuth, useBaseUrl } from "bknd/ui";
// @ts-ignore
import { ClientProvider, useApi, useAuth, useBaseUrl } from "bknd/client";
// biome-ignore lint/style/useImportType: <explanation>
import React from "react";
import { createContext, useContext, useEffect, useMemo, useState } from "react";
// Users will be able to set these props in Studio.
@@ -18,17 +24,6 @@ type BkndContextProps = {
const BkndContextContext = createContext<BkndGlobalContextProps>({} as any);
function getBaseUrlFromWindow() {
if (typeof window === "undefined") {
return "";
}
const protocol = window.location.protocol;
const host = window.location.host;
return `${protocol}//${host}`;
}
// @todo: it's an issue that we need auth, so we cannot make baseurl adjustable (maybe add an option to useAuth with a specific base url?)
export const BkndContext = ({
children,
@@ -36,19 +31,15 @@ export const BkndContext = ({
initialAuth
}: React.PropsWithChildren<BkndContextProps>) => {
const auth = useAuth();
const baseurl = useBaseUrl();
const baseurl = baseUrl ?? useBaseUrl();
const api = useApi({ host: baseurl });
const [data, setData] = useState<BkndGlobalContextProps>({
baseUrl: baseurl,
/*baseUrl: (baseUrl && baseUrl.length > 0 ? baseUrl : getBaseUrlFromWindow()).replace(
/\/+$/,
""
),*/
auth: auth ?? initialAuth,
appConfig: undefined
});
const inEditor = !!usePlasmicCanvasContext();
console.log("context:user", data);
useEffect(() => {
setData((prev) => ({ ...prev, auth: auth }));
@@ -57,8 +48,10 @@ export const BkndContext = ({
useEffect(() => {
(async () => {
if (inEditor) {
const res = await fetch(`${baseurl}/api/system/config`);
const result = (await res.json()) as BkndGlobalContextProps["appConfig"];
const result = await api.system.readConfig();
/*const res = await fetch(`${baseurl}/api/system/config`);
const result = (await res.json()) as BkndGlobalContextProps["appConfig"];*/
console.log("appconfig", result);
setData((prev) => ({ ...prev, appConfig: result }));
}
@@ -101,13 +94,12 @@ export const BkndContext = ({
[baseUrl]
);
console.log("plasmic.bknd.context", data);
console.log("plasmic.bknd.context", { baseUrl });
return (
<GlobalActionsProvider contextName="BkndContext" actions={actions}>
<BkndContextContext.Provider value={data}>
<DataProvider name="bknd" data={data}>
{/*<ClientProvider baseUrl={data.baseUrl}>{children}</ClientProvider>*/}
{children}
<ClientProvider baseUrl={data.baseUrl}>{children}</ClientProvider>
</DataProvider>
</BkndContextContext.Provider>
</GlobalActionsProvider>
@@ -119,8 +111,20 @@ export function usePlasmicBkndContext() {
return context;
}
export const BkndContextMeta = {
export function registerBkndContext(
loader?: { registerGlobalContext: typeof registerGlobalContext },
customMeta?: GlobalContextMeta<BkndContextProps>
) {
if (loader) {
loader.registerGlobalContext(BkndContext, customMeta ?? BkndContextMeta);
} else {
registerGlobalContext(BkndContext, customMeta ?? BkndContextMeta);
}
}
export const BkndContextMeta: GlobalContextMeta<BkndContextProps> = {
name: "BkndContext",
importPath: "@bknd/plasmic",
props: { baseUrl: { type: "string" }, initialAuth: { type: "object" } },
providesData: true,
globalActions: {

View File

@@ -0,0 +1,17 @@
import type { registerComponent, registerGlobalContext } from "@plasmicapp/host";
import { registerImage } from "./components/Image";
import { registerLazyRender } from "./components/LazyRender";
import { registerBkndData } from "./components/data/BkndData";
import { registerBkndContext } from "./contexts/BkndContext";
export function registerAll(loader?: {
registerComponent: typeof registerComponent;
registerGlobalContext: typeof registerGlobalContext;
}) {
registerBkndData(loader);
registerBkndContext(loader);
registerImage(loader);
registerLazyRender(loader);
}
export { registerBkndData, registerBkndContext };

View File

@@ -4,19 +4,23 @@
"lib": ["ESNext", "DOM"],
"target": "ESNext",
"module": "ESNext",
"jsx": "react-jsx",
"jsx": "react",
"allowJs": true,
"moduleResolution": "bundler",
"allowImportingTsExtensions": false,
"verbatimModuleSyntax": true,
"strict": true,
"outDir": "dist",
"declarationDir": "dist",
"declaration": true,
"skipLibCheck": true,
"noFallthroughCasesInSwitch": true,
"noImplicitAny": false,
"noPropertyAccessFromIndexSignature": false
"noPropertyAccessFromIndexSignature": false,
"rootDir": "src",
"baseUrl": "src",
"tsBuildInfoFile": "dist/tsconfig.tsbuildinfo"
},
"include": ["index.ts", "loader.tsx", "components", "contexts"],
"exclude": ["@bknd/app", "@bknd/core", "dist", "node_modules", "build.ts"]
"include": ["src/**/*"],
"exclude": ["bknd", "dist", "node_modules"]
}