mirror of
https://github.com/shishantbiswas/bknd.git
synced 2026-03-16 04:27:21 +00:00
optimize elements by reducing the bundle size required
This commit is contained in:
50
app/build.ts
50
app/build.ts
@@ -1,4 +1,7 @@
|
||||
import fs from "node:fs";
|
||||
|
||||
import { $ } from "bun";
|
||||
import { replace } from "esbuild-plugin-replace";
|
||||
import * as tsup from "tsup";
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
@@ -80,7 +83,8 @@ await tsup.build({
|
||||
"src/ui/index.ts",
|
||||
"src/ui/client/index.ts",
|
||||
"src/ui/elements/index.ts",
|
||||
"src/ui/main.css"
|
||||
"src/ui/main.css",
|
||||
"src/ui/styles.css"
|
||||
],
|
||||
outDir: "dist/ui",
|
||||
external: [
|
||||
@@ -108,6 +112,50 @@ await tsup.build({
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Building UI Elements
|
||||
* - tailwind-merge is mocked, no exclude
|
||||
* - ui/client is external, and after built replaced with "bknd/client"
|
||||
*/
|
||||
await tsup.build({
|
||||
minify,
|
||||
sourcemap,
|
||||
watch,
|
||||
entry: ["src/ui/elements/index.ts"],
|
||||
outDir: "dist/ui/elements",
|
||||
external: [
|
||||
"ui/client",
|
||||
"react",
|
||||
"react-dom",
|
||||
"react/jsx-runtime",
|
||||
"react/jsx-dev-runtime",
|
||||
"use-sync-external-store"
|
||||
],
|
||||
metafile: true,
|
||||
platform: "browser",
|
||||
format: ["esm"],
|
||||
splitting: false,
|
||||
bundle: true,
|
||||
treeshake: true,
|
||||
loader: {
|
||||
".svg": "dataurl"
|
||||
},
|
||||
esbuildOptions: (options) => {
|
||||
options.alias = {
|
||||
// not important for elements, mock to reduce bundle
|
||||
"tailwind-merge": "./src/ui/elements/mocks/tailwind-merge.ts"
|
||||
};
|
||||
},
|
||||
onSuccess: async () => {
|
||||
// manually replace ui/client with bknd/client
|
||||
const path = "./dist/ui/elements/index.js";
|
||||
const bundle = await Bun.file(path).text();
|
||||
await Bun.write(path, bundle.replaceAll("ui/client", "bknd/client"));
|
||||
|
||||
delayTypes();
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Building adapters
|
||||
*/
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
"build:types": "tsc --emitDeclarationOnly && tsc-alias",
|
||||
"updater": "bun x npm-check-updates -ui",
|
||||
"cli": "LOCAL=1 bun src/cli/index.ts",
|
||||
"prepublishOnly": "bun run test && bun run build:all"
|
||||
"prepublishOnly": "bun run types && bun run test && bun run build:all"
|
||||
},
|
||||
"license": "FSL-1.1-MIT",
|
||||
"dependencies": {
|
||||
@@ -29,12 +29,12 @@
|
||||
"dayjs": "^1.11.13",
|
||||
"fast-xml-parser": "^4.4.0",
|
||||
"hono": "^4.6.12",
|
||||
"json-schema-form-react": "^0.0.2",
|
||||
"kysely": "^0.27.4",
|
||||
"liquidjs": "^10.15.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
"oauth4webapi": "^2.11.1",
|
||||
"swr": "^2.2.5",
|
||||
"json-schema-form-react": "^0.0.2"
|
||||
"swr": "^2.2.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@aws-sdk/client-s3": "^3.613.0",
|
||||
@@ -62,6 +62,7 @@
|
||||
"@vitejs/plugin-react": "^4.3.3",
|
||||
"@xyflow/react": "^12.3.2",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"clsx": "^2.1.1",
|
||||
"esbuild-postcss": "^0.0.4",
|
||||
"jotai": "^2.10.1",
|
||||
"open": "^10.1.0",
|
||||
@@ -168,7 +169,8 @@
|
||||
"import": "./dist/adapter/astro/index.js",
|
||||
"require": "./dist/adapter/astro/index.cjs"
|
||||
},
|
||||
"./dist/styles.css": "./dist/ui/main.css",
|
||||
"./dist/main.css": "./dist/ui/main.css",
|
||||
"./dist/styles.css": "./dist/ui/styles.css",
|
||||
"./dist/manifest.json": "./dist/static/.vite/manifest.json"
|
||||
},
|
||||
"publishConfig": {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { type BaseModuleApiOptions, ModuleApi, type PrimaryFieldType } from "modules/ModuleApi";
|
||||
import type { FileWithPath } from "ui/modules/media/components/dropzone/file-selector";
|
||||
import type { FileWithPath } from "ui/elements/media/file-selector";
|
||||
|
||||
export type MediaApiOptions = BaseModuleApiOptions & {};
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import type { TObject, TString } from "@sinclair/typebox";
|
||||
import { type Constructor, Registry } from "core";
|
||||
|
||||
export { MIME_TYPES } from "./storage/mime-types";
|
||||
//export { MIME_TYPES } from "./storage/mime-types";
|
||||
export { guess as guessMimeType } from "./storage/mime-types-tiny";
|
||||
export {
|
||||
Storage,
|
||||
type StorageAdapter,
|
||||
@@ -19,7 +20,7 @@ import { type S3AdapterConfig, StorageS3Adapter } from "./storage/adapters/Stora
|
||||
export { StorageS3Adapter, type S3AdapterConfig, StorageCloudinaryAdapter, type CloudinaryConfig };
|
||||
|
||||
export * as StorageEvents from "./storage/events";
|
||||
export { type FileUploadedEventData } from "./storage/events";
|
||||
export type { FileUploadedEventData } from "./storage/events";
|
||||
export * from "./utils";
|
||||
|
||||
type ClassThatImplements<T> = Constructor<T> & { prototype: T };
|
||||
|
||||
@@ -12,7 +12,6 @@ export type ClientProviderProps = {
|
||||
};
|
||||
|
||||
export const ClientProvider = ({ children, baseUrl, user }: ClientProviderProps) => {
|
||||
//const [actualBaseUrl, setActualBaseUrl] = useState<string | null>(null);
|
||||
const winCtx = useBkndWindowContext();
|
||||
const _ctx_baseUrl = useBaseUrl();
|
||||
let actualBaseUrl = baseUrl ?? _ctx_baseUrl ?? "";
|
||||
@@ -31,6 +30,7 @@ export const ClientProvider = ({ children, baseUrl, user }: ClientProviderProps)
|
||||
console.error("error .....", e);
|
||||
}
|
||||
|
||||
console.log("api init", { host: actualBaseUrl, user: user ?? winCtx.user });
|
||||
const api = new Api({ host: actualBaseUrl, user: user ?? winCtx.user });
|
||||
|
||||
return (
|
||||
|
||||
@@ -73,23 +73,3 @@ export const useAuth = (options?: { baseUrl?: string }): UseAuth => {
|
||||
verify
|
||||
};
|
||||
};
|
||||
|
||||
type AuthStrategyData = Pick<AppAuthSchema, "strategies" | "basepath">;
|
||||
export const useAuthStrategies = (options?: { baseUrl?: string }): Partial<AuthStrategyData> & {
|
||||
loading: boolean;
|
||||
} => {
|
||||
const [data, setData] = useState<AuthStrategyData>();
|
||||
const api = useApi(options?.baseUrl);
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
const res = await api.auth.strategies();
|
||||
//console.log("res", res);
|
||||
if (res.res.ok) {
|
||||
setData(res.body);
|
||||
}
|
||||
})();
|
||||
}, [options?.baseUrl]);
|
||||
|
||||
return { strategies: data?.strategies, basepath: data?.basepath, loading: !data };
|
||||
};
|
||||
|
||||
29
app/src/ui/components/form/Formy/BooleanInputMantine.tsx
Normal file
29
app/src/ui/components/form/Formy/BooleanInputMantine.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
import { Switch } from "@mantine/core";
|
||||
import { forwardRef, useEffect, useState } from "react";
|
||||
|
||||
export const BooleanInputMantine = forwardRef<HTMLInputElement, React.ComponentProps<"input">>(
|
||||
(props, ref) => {
|
||||
const [checked, setChecked] = useState(Boolean(props.value));
|
||||
|
||||
useEffect(() => {
|
||||
setChecked(Boolean(props.value));
|
||||
}, [props.value]);
|
||||
|
||||
function handleCheck(e) {
|
||||
setChecked(e.target.checked);
|
||||
props.onChange?.(e.target.checked);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-row">
|
||||
<Switch
|
||||
ref={ref}
|
||||
checked={checked}
|
||||
onChange={handleCheck}
|
||||
disabled={props.disabled}
|
||||
id={props.id}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
@@ -1,11 +1,10 @@
|
||||
import { Switch } from "@mantine/core";
|
||||
import { getBrowser } from "core/utils";
|
||||
import type { Field } from "data";
|
||||
import { forwardRef, useEffect, useImperativeHandle, useRef, useState } from "react";
|
||||
import { TbCalendar, TbChevronDown, TbInfoCircle } from "react-icons/tb";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import { useEvent } from "../../hooks/use-event";
|
||||
import { IconButton } from "../buttons/IconButton";
|
||||
import { IconButton } from "ui/components/buttons/IconButton";
|
||||
import { useEvent } from "ui/hooks/use-event";
|
||||
|
||||
export const Group: React.FC<React.ComponentProps<"div"> & { error?: boolean }> = ({
|
||||
error,
|
||||
@@ -131,17 +130,6 @@ export const BooleanInput = forwardRef<HTMLInputElement, React.ComponentProps<"i
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-row">
|
||||
<Switch
|
||||
ref={ref}
|
||||
checked={checked}
|
||||
onChange={handleCheck}
|
||||
disabled={props.disabled}
|
||||
id={props.id}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
/*return (
|
||||
<div className="h-11 flex items-center">
|
||||
<input
|
||||
{...props}
|
||||
@@ -153,7 +141,7 @@ export const BooleanInput = forwardRef<HTMLInputElement, React.ComponentProps<"i
|
||||
disabled={props.disabled}
|
||||
/>
|
||||
</div>
|
||||
);*/
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
17
app/src/ui/components/form/Formy/index.ts
Normal file
17
app/src/ui/components/form/Formy/index.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { BooleanInputMantine } from "./BooleanInputMantine";
|
||||
import { DateInput, Input, Textarea } from "./components";
|
||||
|
||||
export const formElementFactory = (element: string, props: any) => {
|
||||
switch (element) {
|
||||
case "date":
|
||||
return DateInput;
|
||||
case "boolean":
|
||||
return BooleanInputMantine;
|
||||
case "textarea":
|
||||
return Textarea;
|
||||
default:
|
||||
return Input;
|
||||
}
|
||||
};
|
||||
|
||||
export * from "./components";
|
||||
@@ -1,13 +1,13 @@
|
||||
import type { ValueError } from "@sinclair/typebox/value";
|
||||
import type { AppAuthOAuthStrategy, AppAuthSchema } from "auth/auth-schema";
|
||||
import clsx from "clsx";
|
||||
import { type TSchema, Type, Value } from "core/utils";
|
||||
import { Form, type Validator } from "json-schema-form-react";
|
||||
import { transform } from "lodash-es";
|
||||
import type { ComponentPropsWithoutRef } from "react";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import { Button } from "ui/components/buttons/Button";
|
||||
import { Group, Input, Label } from "ui/components/form/Formy";
|
||||
import { SocialLink } from "ui/modules/auth/SocialLink";
|
||||
import { Group, Input, Label } from "ui/components/form/Formy/components";
|
||||
import { SocialLink } from "./SocialLink";
|
||||
|
||||
export type LoginFormProps = Omit<ComponentPropsWithoutRef<"form">, "onSubmit" | "action"> & {
|
||||
className?: string;
|
||||
@@ -86,7 +86,7 @@ export function AuthForm({
|
||||
schema={schema}
|
||||
validator={validator}
|
||||
validationMode="change"
|
||||
className={twMerge("flex flex-col gap-3 w-full", className)}
|
||||
className={clsx("flex flex-col gap-3 w-full", className)}
|
||||
>
|
||||
{({ errors, submitting }) => (
|
||||
<>
|
||||
@@ -1,8 +1,6 @@
|
||||
import type { ReactNode } from "react";
|
||||
import { useAuthStrategies } from "ui/client/schema/auth/use-auth";
|
||||
import { Logo } from "ui/components/display/Logo";
|
||||
import { Link } from "ui/components/wouter/Link";
|
||||
import { AuthForm } from "ui/modules/auth/AuthForm";
|
||||
import { useAuthStrategies } from "../hooks/use-auth";
|
||||
import { AuthForm } from "./AuthForm";
|
||||
|
||||
export type AuthScreenProps = {
|
||||
method?: "POST" | "GET";
|
||||
@@ -18,13 +16,7 @@ export function AuthScreen({ method = "POST", action = "login", logo, intro }: A
|
||||
<div className="flex flex-1 flex-col select-none h-dvh w-dvw justify-center items-center bknd-admin">
|
||||
{!loading && (
|
||||
<div className="flex flex-col gap-4 items-center w-96 px-6 py-7">
|
||||
{typeof logo !== "undefined" ? (
|
||||
logo
|
||||
) : (
|
||||
<Link href={"/"} className="link">
|
||||
<Logo scale={0.25} />
|
||||
</Link>
|
||||
)}
|
||||
{logo ? logo : null}
|
||||
{typeof intro !== "undefined" ? (
|
||||
intro
|
||||
) : (
|
||||
9
app/src/ui/elements/auth/index.ts
Normal file
9
app/src/ui/elements/auth/index.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { AuthForm } from "./AuthForm";
|
||||
import { AuthScreen } from "./AuthScreen";
|
||||
import { SocialLink } from "./SocialLink";
|
||||
|
||||
export const Auth = {
|
||||
Screen: AuthScreen,
|
||||
Form: AuthForm,
|
||||
SocialLink: SocialLink
|
||||
};
|
||||
23
app/src/ui/elements/hooks/use-auth.ts
Normal file
23
app/src/ui/elements/hooks/use-auth.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import type { AppAuthSchema } from "auth/auth-schema";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useApi } from "ui/client";
|
||||
|
||||
type AuthStrategyData = Pick<AppAuthSchema, "strategies" | "basepath">;
|
||||
export const useAuthStrategies = (options?: { baseUrl?: string }): Partial<AuthStrategyData> & {
|
||||
loading: boolean;
|
||||
} => {
|
||||
const [data, setData] = useState<AuthStrategyData>();
|
||||
const api = useApi(options?.baseUrl);
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
const res = await api.auth.strategies();
|
||||
//console.log("res", res);
|
||||
if (res.res.ok) {
|
||||
setData(res.body);
|
||||
}
|
||||
})();
|
||||
}, [options?.baseUrl]);
|
||||
|
||||
return { strategies: data?.strategies, basepath: data?.basepath, loading: !data };
|
||||
};
|
||||
@@ -1,2 +1,2 @@
|
||||
export { Auth } from "ui/modules/auth/index";
|
||||
export { Auth } from "./auth";
|
||||
export * from "./media";
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
import { PreviewWrapperMemoized } from "ui/modules/media/components/dropzone/Dropzone";
|
||||
import { DropzoneContainer } from "ui/modules/media/components/dropzone/DropzoneContainer";
|
||||
|
||||
export const Media = {
|
||||
Dropzone: DropzoneContainer,
|
||||
Preview: PreviewWrapperMemoized
|
||||
};
|
||||
|
||||
export type {
|
||||
PreviewComponentProps,
|
||||
FileState,
|
||||
DropzoneProps,
|
||||
DropzoneRenderProps
|
||||
} from "ui/modules/media/components/dropzone/Dropzone";
|
||||
export type { DropzoneContainerProps } from "ui/modules/media/components/dropzone/DropzoneContainer";
|
||||
@@ -4,13 +4,8 @@ import type { TAppMediaConfig } from "media/media-schema";
|
||||
import { useId } from "react";
|
||||
import { useApi, useBaseUrl, useEntityQuery, useInvalidate } from "ui/client";
|
||||
import { useEvent } from "ui/hooks/use-event";
|
||||
import {
|
||||
Dropzone,
|
||||
type DropzoneProps,
|
||||
type DropzoneRenderProps,
|
||||
type FileState
|
||||
} from "ui/modules/media/components/dropzone/Dropzone";
|
||||
import { mediaItemsToFileStates } from "ui/modules/media/helper";
|
||||
import { Dropzone, type DropzoneProps, type DropzoneRenderProps, type FileState } from "./Dropzone";
|
||||
import { mediaItemsToFileStates } from "./helper";
|
||||
|
||||
export type DropzoneContainerProps = {
|
||||
children?: (props: DropzoneRenderProps) => JSX.Element;
|
||||
@@ -4,7 +4,7 @@
|
||||
* MIT License (2020 Roland Groza)
|
||||
*/
|
||||
|
||||
import { MIME_TYPES } from "media";
|
||||
import { guess } from "media/storage/mime-types-tiny";
|
||||
|
||||
const FILES_TO_IGNORE = [
|
||||
// Thumbnail cache files for macOS and Windows
|
||||
@@ -47,10 +47,8 @@ function withMimeType(file: FileWithPath) {
|
||||
console.log("withMimeType", name, hasExtension);
|
||||
|
||||
if (hasExtension && !file.type) {
|
||||
const ext = name.split(".").pop()!.toLowerCase();
|
||||
const type = MIME_TYPES.get(ext);
|
||||
|
||||
console.log("withMimeType:in", ext, type);
|
||||
const type = guess(name);
|
||||
console.log("guessed", type);
|
||||
|
||||
if (type) {
|
||||
Object.defineProperty(file, "type", {
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { MediaFieldSchema } from "media/AppMedia";
|
||||
import type { FileState } from "./components/dropzone/Dropzone";
|
||||
import type { FileState } from "./Dropzone";
|
||||
|
||||
export function mediaItemToFileState(
|
||||
item: MediaFieldSchema,
|
||||
15
app/src/ui/elements/media/index.ts
Normal file
15
app/src/ui/elements/media/index.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { PreviewWrapperMemoized } from "./Dropzone";
|
||||
import { DropzoneContainer } from "./DropzoneContainer";
|
||||
|
||||
export const Media = {
|
||||
Dropzone: DropzoneContainer,
|
||||
Preview: PreviewWrapperMemoized
|
||||
};
|
||||
|
||||
export type {
|
||||
PreviewComponentProps,
|
||||
FileState,
|
||||
DropzoneProps,
|
||||
DropzoneRenderProps
|
||||
} from "./Dropzone";
|
||||
export type { DropzoneContainerProps } from "./DropzoneContainer";
|
||||
3
app/src/ui/elements/mocks/tailwind-merge.ts
Normal file
3
app/src/ui/elements/mocks/tailwind-merge.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export function twMerge(...classes: string[]) {
|
||||
return classes.filter(Boolean).join(" ");
|
||||
}
|
||||
@@ -1,211 +1,77 @@
|
||||
@import "./components/form/json-schema/styles.css";
|
||||
@import "@xyflow/react/dist/style.css";
|
||||
@import "@mantine/core/styles.css";
|
||||
@import "@mantine/notifications/styles.css";
|
||||
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
html.fixed,
|
||||
html.fixed body {
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
position: fixed;
|
||||
overflow: hidden;
|
||||
overscroll-behavior-x: contain;
|
||||
touch-action: none;
|
||||
}
|
||||
|
||||
#bknd-admin,
|
||||
.bknd-admin {
|
||||
--color-primary: 9 9 11; /* zinc-950 */
|
||||
--color-background: 250 250 250; /* zinc-50 */
|
||||
--color-muted: 228 228 231; /* ? */
|
||||
--color-darkest: 0 0 0; /* black */
|
||||
--color-lightest: 255 255 255; /* white */
|
||||
|
||||
&.dark {
|
||||
--color-primary: 250 250 250; /* zinc-50 */
|
||||
--color-background: 30 31 34;
|
||||
--color-muted: 47 47 52;
|
||||
--color-darkest: 255 255 255; /* white */
|
||||
--color-lightest: 24 24 27; /* black */
|
||||
}
|
||||
|
||||
@mixin light {
|
||||
--mantine-color-body: rgb(250 250 250);
|
||||
}
|
||||
@mixin dark {
|
||||
--mantine-color-body: rgb(9 9 11);
|
||||
}
|
||||
|
||||
table {
|
||||
font-size: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
font-size: 14px;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
overscroll-behavior-y: none;
|
||||
}
|
||||
|
||||
#bknd-admin {
|
||||
@apply bg-background text-primary overflow-hidden h-dvh w-dvw;
|
||||
@apply bg-background text-primary overflow-hidden h-dvh w-dvw;
|
||||
|
||||
::selection {
|
||||
@apply bg-muted;
|
||||
}
|
||||
::selection {
|
||||
@apply bg-muted;
|
||||
}
|
||||
|
||||
input {
|
||||
&::selection {
|
||||
@apply bg-primary/15;
|
||||
}
|
||||
}
|
||||
input {
|
||||
&::selection {
|
||||
@apply bg-primary/15;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
body,
|
||||
#bknd-admin {
|
||||
@apply flex flex-1 flex-col h-dvh w-dvw;
|
||||
@apply flex flex-1 flex-col h-dvh w-dvw;
|
||||
}
|
||||
|
||||
@layer components {
|
||||
.link {
|
||||
@apply transition-colors active:translate-y-px;
|
||||
}
|
||||
.link {
|
||||
@apply transition-colors active:translate-y-px;
|
||||
}
|
||||
|
||||
.img-responsive {
|
||||
@apply max-h-full w-auto;
|
||||
}
|
||||
.img-responsive {
|
||||
@apply max-h-full w-auto;
|
||||
}
|
||||
|
||||
/**
|
||||
* debug classes
|
||||
*/
|
||||
.bordered-red {
|
||||
@apply border-2 border-red-500;
|
||||
}
|
||||
/**
|
||||
* debug classes
|
||||
*/
|
||||
.bordered-red {
|
||||
@apply border-2 border-red-500;
|
||||
}
|
||||
|
||||
.bordered-green {
|
||||
@apply border-2 border-green-500;
|
||||
}
|
||||
.bordered-green {
|
||||
@apply border-2 border-green-500;
|
||||
}
|
||||
|
||||
.bordered-blue {
|
||||
@apply border-2 border-blue-500;
|
||||
}
|
||||
.bordered-blue {
|
||||
@apply border-2 border-blue-500;
|
||||
}
|
||||
|
||||
.bordered-violet {
|
||||
@apply border-2 border-violet-500;
|
||||
}
|
||||
.bordered-violet {
|
||||
@apply border-2 border-violet-500;
|
||||
}
|
||||
|
||||
.bordered-yellow {
|
||||
@apply border-2 border-yellow-500;
|
||||
}
|
||||
.bordered-yellow {
|
||||
@apply border-2 border-yellow-500;
|
||||
}
|
||||
}
|
||||
|
||||
@layer utilities {
|
||||
}
|
||||
|
||||
/* Hide scrollbar for Chrome, Safari and Opera */
|
||||
.app-scrollbar::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
/* Hide scrollbar for IE, Edge and Firefox */
|
||||
.app-scrollbar {
|
||||
-ms-overflow-style: none; /* IE and Edge */
|
||||
scrollbar-width: none; /* Firefox */
|
||||
}
|
||||
|
||||
div[data-radix-scroll-area-viewport] > div:first-child {
|
||||
display: block !important;
|
||||
min-width: 100% !important;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
/* hide calendar icon on inputs */
|
||||
input[type="datetime-local"]::-webkit-calendar-picker-indicator,
|
||||
input[type="date"]::-webkit-calendar-picker-indicator {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* cm */
|
||||
.cm-editor {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.animate-fade-in {
|
||||
animation: fadeInAnimation 200ms ease;
|
||||
}
|
||||
@keyframes fadeInAnimation {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
input[readonly]::placeholder,
|
||||
input[disabled]::placeholder {
|
||||
opacity: 0.1;
|
||||
}
|
||||
|
||||
.react-flow__pane,
|
||||
.react-flow__renderer,
|
||||
.react-flow__node,
|
||||
.react-flow__edge {
|
||||
cursor: inherit !important;
|
||||
.drag-handle {
|
||||
cursor: grab;
|
||||
}
|
||||
}
|
||||
.react-flow .react-flow__edge path,
|
||||
.react-flow__connectionline path {
|
||||
stroke-width: 2;
|
||||
}
|
||||
|
||||
.mantine-TextInput-wrapper input {
|
||||
font-family: inherit;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.cm-editor {
|
||||
background: transparent;
|
||||
}
|
||||
.cm-editor.cm-focused {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.flex-animate {
|
||||
transition: flex-grow 0.2s ease, background-color 0.2s ease;
|
||||
}
|
||||
.flex-initial {
|
||||
flex: 0 1 auto;
|
||||
}
|
||||
.flex-open {
|
||||
flex: 1 1 0;
|
||||
}
|
||||
|
||||
#bknd-admin,
|
||||
.bknd-admin {
|
||||
/* Chrome, Edge, and Safari */
|
||||
& *::-webkit-scrollbar {
|
||||
@apply w-1;
|
||||
&:horizontal {
|
||||
@apply h-px;
|
||||
}
|
||||
}
|
||||
/* Chrome, Edge, and Safari */
|
||||
& *::-webkit-scrollbar {
|
||||
@apply w-1;
|
||||
&:horizontal {
|
||||
@apply h-px;
|
||||
}
|
||||
}
|
||||
|
||||
& *::-webkit-scrollbar-track {
|
||||
@apply bg-transparent w-1;
|
||||
}
|
||||
& *::-webkit-scrollbar-track {
|
||||
@apply bg-transparent w-1;
|
||||
}
|
||||
|
||||
& *::-webkit-scrollbar-thumb {
|
||||
@apply bg-primary/25;
|
||||
}
|
||||
& *::-webkit-scrollbar-thumb {
|
||||
@apply bg-primary/25;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import * as React from "react";
|
||||
import * as ReactDOM from "react-dom/client";
|
||||
import Admin from "./Admin";
|
||||
import "./main.css";
|
||||
import "./styles.css";
|
||||
|
||||
ReactDOM.createRoot(document.getElementById("root")!).render(
|
||||
<React.StrictMode>
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
import { AuthForm } from "ui/modules/auth/AuthForm";
|
||||
import { AuthScreen } from "ui/modules/auth/AuthScreen";
|
||||
import { SocialLink } from "ui/modules/auth/SocialLink";
|
||||
|
||||
export const Auth = {
|
||||
Screen: AuthScreen,
|
||||
Form: AuthForm,
|
||||
SocialLink: SocialLink
|
||||
};
|
||||
@@ -1,7 +1,18 @@
|
||||
import { Logo } from "ui/components/display/Logo";
|
||||
import { Link } from "ui/components/wouter/Link";
|
||||
import { Auth } from "ui/elements";
|
||||
import { useBrowserTitle } from "ui/hooks/use-browser-title";
|
||||
import { AuthScreen } from "ui/modules/auth/AuthScreen";
|
||||
|
||||
export function AuthLogin() {
|
||||
useBrowserTitle(["Login"]);
|
||||
return <AuthScreen action="login" />;
|
||||
return (
|
||||
<Auth.Screen
|
||||
action="login"
|
||||
logo={
|
||||
<Link href={"/"} className="link">
|
||||
<Logo scale={0.25} />
|
||||
</Link>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
133
app/src/ui/styles.css
Normal file
133
app/src/ui/styles.css
Normal file
@@ -0,0 +1,133 @@
|
||||
@import "./components/form/json-schema/styles.css";
|
||||
@import "@xyflow/react/dist/style.css";
|
||||
@import "@mantine/core/styles.css";
|
||||
@import "@mantine/notifications/styles.css";
|
||||
|
||||
html.fixed,
|
||||
html.fixed body {
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
position: fixed;
|
||||
overflow: hidden;
|
||||
overscroll-behavior-x: contain;
|
||||
touch-action: none;
|
||||
}
|
||||
|
||||
#bknd-admin,
|
||||
.bknd-admin {
|
||||
--color-primary: 9 9 11; /* zinc-950 */
|
||||
--color-background: 250 250 250; /* zinc-50 */
|
||||
--color-muted: 228 228 231; /* ? */
|
||||
--color-darkest: 0 0 0; /* black */
|
||||
--color-lightest: 255 255 255; /* white */
|
||||
|
||||
&.dark {
|
||||
--color-primary: 250 250 250; /* zinc-50 */
|
||||
--color-background: 30 31 34;
|
||||
--color-muted: 47 47 52;
|
||||
--color-darkest: 255 255 255; /* white */
|
||||
--color-lightest: 24 24 27; /* black */
|
||||
}
|
||||
|
||||
@mixin light {
|
||||
--mantine-color-body: rgb(250 250 250);
|
||||
}
|
||||
@mixin dark {
|
||||
--mantine-color-body: rgb(9 9 11);
|
||||
}
|
||||
|
||||
table {
|
||||
font-size: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
font-size: 14px;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
overscroll-behavior-y: none;
|
||||
}
|
||||
|
||||
/* Hide scrollbar for Chrome, Safari and Opera */
|
||||
.app-scrollbar::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
/* Hide scrollbar for IE, Edge and Firefox */
|
||||
.app-scrollbar {
|
||||
-ms-overflow-style: none; /* IE and Edge */
|
||||
scrollbar-width: none; /* Firefox */
|
||||
}
|
||||
|
||||
div[data-radix-scroll-area-viewport] > div:first-child {
|
||||
display: block !important;
|
||||
min-width: 100% !important;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
/* hide calendar icon on inputs */
|
||||
input[type="datetime-local"]::-webkit-calendar-picker-indicator,
|
||||
input[type="date"]::-webkit-calendar-picker-indicator {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* cm */
|
||||
.cm-editor {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.animate-fade-in {
|
||||
animation: fadeInAnimation 200ms ease;
|
||||
}
|
||||
@keyframes fadeInAnimation {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
input[readonly]::placeholder,
|
||||
input[disabled]::placeholder {
|
||||
opacity: 0.1;
|
||||
}
|
||||
|
||||
.react-flow__pane,
|
||||
.react-flow__renderer,
|
||||
.react-flow__node,
|
||||
.react-flow__edge {
|
||||
cursor: inherit !important;
|
||||
.drag-handle {
|
||||
cursor: grab;
|
||||
}
|
||||
}
|
||||
.react-flow .react-flow__edge path,
|
||||
.react-flow__connectionline path {
|
||||
stroke-width: 2;
|
||||
}
|
||||
|
||||
.mantine-TextInput-wrapper input {
|
||||
font-family: inherit;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.cm-editor {
|
||||
background: transparent;
|
||||
}
|
||||
.cm-editor.cm-focused {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.flex-animate {
|
||||
transition: flex-grow 0.2s ease, background-color 0.2s ease;
|
||||
}
|
||||
.flex-initial {
|
||||
flex: 0 1 auto;
|
||||
}
|
||||
.flex-open {
|
||||
flex: 1 1 0;
|
||||
}
|
||||
@@ -19,6 +19,11 @@
|
||||
"trailingCommas": "none"
|
||||
}
|
||||
},
|
||||
"css": {
|
||||
"formatter": {
|
||||
"indentWidth": 3
|
||||
}
|
||||
},
|
||||
"files": {
|
||||
"ignore": [
|
||||
"**/node_modules/**",
|
||||
@@ -74,4 +79,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user